Page through data one screen at a time and render each page however you like. paginate owns the navigation loop, never the data — you give it a command that returns one page and a command that renders it, so the same pager works for a local file and a paginated HTTP endpoint. Nothing is ever loaded in full, so a file larger than memory pages exactly as cheaply as a small one.
aux4 aux4 pkger install aux4/paginate
# A local file, three rows at a time, drawn as a table
aux4 paginate 'cat orders.json | aux4 json page --offset {offset} --limit {limit}' \
--render 'aux4 render table "id,name,total"' \
--limit 3
# A paginated endpoint that wraps its rows in an envelope
aux4 paginate 'aux4 curl "https://api.example.com/items?page={page}&limit={limit}"' \
--data '$.data' \
--totalPages '$.pagination.totalPages' \
--render 'aux4 render table "id,name"'
The pager keeps one piece of state: the current page index and the page size. Everything a fetch command might need is derived from those, so offset-style and page-style APIs are both served without a mode flag — your command names whichever it wants.
| Placeholder | Value | |---|---| | {page} | current page, 1-based | | {pageIndex} | current page, 0-based | | {offset} | pageIndex * limit | | {limit}, {pageSize} | records per page |
Placeholders use single braces. ${page} would not survive: aux4 expands ${...} in parameter values before the command reaches the pager, so a simple ${page} arrives empty. Replacement matches whole tokens only, so a JSON body such as -d '{"page":1}' is left alone.
| Key | Action | |---|---| | > . n space → | next page | | < , p ← | previous page | | [ g Home | first page | | ] G End | last page — only when the total is known | | q Ctrl-C | quit |
Boundaries are clamped: < on the first page stays put rather than requesting a negative offset, and > will not move past the end.
] works only when --total or --totalPages resolves. The pager never guesses a total, and it hides the ] last hint when there is none — so "there is no last page" stays distinguishable from "the key did nothing".
Most APIs wrap their rows:
{
"data": [],
"pagination": {
"page": 1,
"total": 240,
"totalPages": 24
}
}
--data points at the array, using the same path syntax as aux4 json get ($, $.data, $.results.0, $.page[0].items). The renderer only ever receives the extracted array, never the envelope — so aux4 render table gets exactly what it expects without knowing pagination happened.
--total, --totalPages and --hasMore point at the metadata. With metadata the status line reads:
page 3 of 24 · 240 items < prev > next [ first ] last q quit
and without it, simply page 3.
--hasMore is worth setting even though it does not enable ]: without it the only way to detect the end is fetching an empty page.
With no --data, the fetch output is passed to the renderer untouched, so sources that are not JSON still work.
aux4 paginate <fetch> [--render <cmd>] [--data <path>] [--limit <n>]
fetch — command that returns one page (required)--render — command each page is piped through. Default: print the page as-is--data — path to the array in the response. Default: $--limit — records per page. Default: 10--total — path to the total record count, e.g. $.pagination.total--totalPages — path to the total page count, e.g. $.pagination.totalPages--hasMore — path to a boolean saying whether more pages follow--page — page to start on. Default: 1Piped, redirected, or running in CI, paginate prints the starting page once and exits instead of waiting for keys. It never blocks a script.
aux4 paginate 'cat orders.json | aux4 json page --offset {offset} --limit {limit}' --limit 2 > page1.json
Any command that reads JSON on stdin works — the renderer never knows it is being paginated:
--render 'aux4 render table "id,status,total"'
--render 'aux4 render list "id" --secondary "customer.name"'
--render 'aux4 render kv'
--render 'aux4 json select "id,status"'
Each renderer takes its own arguments — render table and render list both take the field spec positionally, and render list requires one.
Apache-2.0