Turn a Pile of Receipts into a Spending Report

Introduction

A folder of receipt photos tells you nothing until you add the numbers up. This post turns those images into a queryable ledger: OCR each receipt, store the vendor, date, and total in a local document store, then report total spend by category — all from the shell.

Install the packages

aux4 aux4 pkger install aux4/image-text aux4/repository aux4/2table

Step 1: read a receipt

aux4 image text runs OCR and prints the text:

aux4 image text whole-foods.png
WHOLE FOODS MARKET

Date: 2026-06-12

TOTAL 54.20

The vendor is the first line, the date and total are labeled — everything you need for a ledger entry.

Step 2: wrap extraction in a command

Rather than repeat that shell for every receipt, put the extraction in an .aux4 file as a receipt add command. Each set: line with a ! prefix runs a command and captures its output into a variable; the last line stores the record. Create .aux4:

{
  "profiles": [
    {
      "name": "main",
      "commands": [
        { "name": "receipt", "execute": ["profile:receipt"], "help": { "text": "Receipt commands" } }
      ]
    },
    {
      "name": "receipt",
      "commands": [
        {
          "name": "add",
          "execute": [
            "set:vendor=!aux4 image text ${file} | awk 'NR==1 {printf \"%s\", $0}'",
            "set:date=!aux4 image text ${file} | awk '/Date/ {printf \"%s\", $2}'",
            "set:amount=!aux4 image text ${file} | awk '/TOTAL/ {printf \"%s\", $NF}'",
            "aux4 repository write receipts --data '{\"vendor\": \"${vendor}\", \"date\": \"${date}\", \"amount\": ${amount}, \"category\": \"${category}\"}'"
          ],
          "help": {
            "text": "OCR a receipt and store it in the receipts repository",
            "variables": [
              { "name": "file", "text": "Receipt image path", "arg": true },
              { "name": "category", "text": "Spending category", "default": "Uncategorized" }
            ]
          }
        }
      ]
    }
  ]
}

set:vendor=!<command> captures the command's output — so vendor, date, and amount come straight from the OCR text, then repository write stores them as one record. aux4 picks up the .aux4 in your working directory, so the command is ready:

aux4 receipt add whole-foods.png --category Groceries
724AB72A-DF21-4F8C-9BF4-6733D705D277

It returns the stored record's id. Run it for the rest of the pile, tagging each with a category:

aux4 receipt add trader-joes.png --category Groceries
aux4 receipt add shell.png --category Fuel
aux4 receipt add starbucks.png --category Coffee

Step 3: build the report

Now the receipts are data. List the whole ledger with aux4 2table:

aux4 repository read receipts | aux4 2table vendor,date,amount,category
 vendor              date        amount  category
 WHOLE FOODS MARKET  2026-06-12    54.2  Groceries
 TRADER JOES         2026-06-20    32.1  Groceries
 SHELL STATION       2026-06-15      41  Fuel
 STARBUCKS           2026-06-18    8.75  Coffee

Filter to one category with a SQL-like expression:

aux4 repository find receipts --expr "category = 'Groceries'" | aux4 2table vendor,amount
 vendor              amount
 WHOLE FOODS MARKET    54.2
 TRADER JOES           32.1

And roll it up — grand total and per-category totals — with a touch of jq for the arithmetic:

aux4 repository read receipts | jq '[.[].amount] | add'
136.05
aux4 repository read receipts \
  | jq -c 'group_by(.category)[] | {category: .[0].category, total: (([.[].amount]|add)*100|round/100), count: length}'
{"category":"Coffee","total":8.75,"count":1}
{"category":"Fuel","total":41,"count":1}
{"category":"Groceries","total":86.3,"count":2}

Conclusion

A stack of images becomes a spending report: your own aux4 receipt add command OCRs and stores each receipt with aux4 image text and aux4 repository, and aux4 2table prints the ledger. Wrapping the extraction in an .aux4 command turns a fiddly shell snippet into one reusable verb — and because every receipt is now a queryable record, changing the report is just a different query, by month, by vendor, or by category.

See Also