>_TEMPRS — PUSH YOUR DATA. OWN YOUR TEMP FILES.
A stack-based temporary file manager for the shell. Pipe data into tp, get a numbered tempfile on top of a persistent stack. Address files by position (1, -1) or by tag (@mydata). Stack operations mirror Perl/Ruby arrays: push, pop, shift, unshift, move, swap, duplicate, reverse, sort. The master record is null-byte delimited, atomically renamed, and flock-protected so concurrent shells can't corrupt it. Both tp and temprs binaries are shipped from one Cargo target.
Quickstart
Install from crates.io or build from source. tp is the short alias; temprs is the long name. Both binaries are identical.
# install cargo install temprs # from source git clone https://github.com/MenkeTechnologies/temprs cd temprs && cargo build --release # push stdin to a new tempfile on top of stack echo "hello" | tp # list the stack tp -l # read top of stack to stdout tp | wc -l # read tempfile at index 1 tp -o 1 | head -5 # tag a tempfile so you can recall it by name ps aux | tp -w procs tp -o procs | grep firefox
Full install + usage live in the README; the man page is shipped at man/temprs.1.
Stack architecture
Tempfiles are ordered in a persistent stack. Top of stack is the newest entry. Indices are 1-based ascending from the bottom; negative indices count from the top (-1 = top, -stack_size = bottom). Index 0 is always invalid.
Files live in $TMPDIR/temprs/ (override with TEMPRS_DIR). The master record (master) maps index → filename → optional tag. Stack position is reconstructed from the master on every invocation, so multiple shells share one canonical view.
Dual indexing — by position or by @name
Every operation that takes an INDEX also accepts a tag name. Resolution order: numeric parse first, then tag lookup. Names are unique across the stack and travel with their files through every stack mutation (move, swap, duplicate). Names render with a @ prefix in listings.
# tag on creation ps aux | tp -w procs # tag on existing file (rename) tp -R 1 newname tp -R procs procs2 # use the tag anywhere INDEX is accepted tp -o procs tp -r procs tp -A procs < more.txt tp -M procs 1 tp -S procs other tp -x procs tp --head procs 10 tp --replace procs old new
Operations — full surface
I/O
tp (push stdin), tp FILE (load file), tp -i N (write into N), tp -o N (read N to stdout), tp -v (verbose = also write to stdout), tp -A N (append to N).
Stack mutation
-p pop, -u unshift, -s shift, -a N insert at N, -r N remove, -M src dst move, -S a b swap, -x N duplicate to top.
Bulk
--rev reverse the stack, --sort name|size|mtime sort, -c purge all, --expire HOURS purge by mtime (fractional hours OK: 0.5).
Listing
-l list, -L list with contents, -n numbered, -N numbered with contents, -k count, -d show temprs directory, -m show master file.
Inspect
-I N metadata, --head N [LINES], --tail N [LINES], --wc N, --size N, --path N (raw filesystem path for shell substitution).
Search & transform
-g PATTERN grep across all tempfiles (exit 0/1), --replace N PAT REPL find-and-replace in place (prints replacement count).
Combine
-C a b c concat tempfiles to stdout in order (indices and names freely mixed), -D a b unified diff of two tempfiles.
Tagging
-w NAME tag a new tempfile on creation, -R src NAME rename a tag, names render as @name in listings.
Editor
-e N open in $EDITOR (falls back to vi), works with -1 for top-of-stack.
Flag reference
The same flag set is bound under both binaries. Long forms shown where they exist; tp -h prints the cyberpunk banner + grouped help.
| Flag | Argument | Behavior |
|---|---|---|
| -i, --input | INDEX | Write stdin into tempfile at INDEX |
| -o, --output | INDEX | Read tempfile at INDEX to stdout |
| -a, --add | INDEX [FILE] | Insert new tempfile at INDEX (from stdin or FILE) |
| -r, --remove | INDEX | Remove tempfile at INDEX |
| -p, --pop | Pop top of stack | |
| -u, --unshift | Push to bottom (stdin in, no stdout) | |
| -s, --shift | Shift from bottom | |
| -A, --append | INDEX | Append stdin to tempfile at INDEX |
| -v, --verbose | Also echo data to stdout on write | |
| -w, --write-name | NAME | Tag new tempfile with NAME |
| -R, --rename | SRC NAME | Rename tag (SRC = index or current name) |
| -l, --list | List stack | |
| -L | List stack with contents | |
| -n | List stack numbered | |
| -N | List stack numbered with contents | |
| -k, --count | Print stack size | |
| -d, --dir | Print temprs directory | |
| -m, --master | Print master record path | |
| -c, --clear | Purge all tempfiles | |
| --expire | HOURS | Purge tempfiles older than HOURS (fractional OK) |
| -e, --edit | INDEX | Open INDEX in $EDITOR (fallback vi) |
| -I, --info | INDEX | Show metadata for INDEX |
| --head | INDEX [LINES] | Head of INDEX (default 10) |
| --tail | INDEX [LINES] | Tail of INDEX (default 10) |
| --wc | INDEX | Line count of INDEX |
| --size | INDEX | Byte size of INDEX |
| --path | INDEX | Filesystem path of INDEX (for cat "$(tp --path 1)") |
| -g, --grep | PATTERN | Grep all tempfiles (exit 0 on match, 1 on none) |
| --replace | INDEX PAT REPL | Replace PAT with REPL in INDEX, print replacement count |
| -C, --concat | INDEX... | Concatenate listed tempfiles to stdout |
| -D, --diff | A B | Unified diff of two tempfiles |
| -M, --move | SRC DST | Move tempfile from SRC to DST position |
| -S, --swap | A B | Swap two tempfiles |
| -x, --dup | INDEX | Duplicate INDEX onto top of stack |
| --rev | Reverse the entire stack | |
| --sort | name|size|mtime | Sort the stack |
Data integrity
The master record is the single source of truth for the stack. Three layered protections prevent corruption under concurrent access:
- Null-byte delimited format — fields separated by
\0, records by\0\0. Filenames containing newlines, tabs, spaces, or any other shell-metacharacters parse correctly. No quoting, no escaping. - Atomic writes — the master is written to a sibling temp file and renamed into place. A crash mid-write leaves the previous master intact; readers never see a partial record.
- Exclusive file locking —
fs2::FileExt::lock_exclusivewraps every read-modify-write. Two shells racing on the same stack serialize cleanly; neither sees a half-mutated state. - Auto-recovery — corrupt or empty records (e.g. from an aborted external edit) are silently skipped on read and cleaned up on the next write.
Environment
| Variable | Effect |
|---|---|
| TEMPRS_DIR | Override the tempfile + master record directory. Default: $TMPDIR/temprs (or /tmp/temprs if $TMPDIR is unset). |
| EDITOR | Used by tp -e INDEX. Falls back to vi. |
Shell integration
A zsh completion lives at completions/_temprs and dynamically resolves stack indices, file names, and @name tags — tab-completion sees the current stack, not a static word list.
# install into your fpath cp completions/_temprs /usr/local/share/zsh/site-functions/_tp # or extend fpath in .zshrc fpath=(/path/to/temprs/completions $fpath) autoload -Uz compinit && compinit
A groff man page is shipped at man/temprs.1 — install to $MANPATH/man1/ for man tp.
Pipeline examples
# capture, transform, recall ps aux | tp -w procs tp -o procs | awk '$3 > 5' # diff two captures curl -s api/v1/state | tp -w before make deploy curl -s api/v1/state | tp -w after tp -D before after # build a working set, then dump it grep -r TODO src | tp -w todos grep -r FIXME src | tp -w fixmes tp -C todos fixmes | sort -u # edit a captured payload, then re-emit curl -s config.json | tp -w cfg tp -e cfg tp -o cfg | curl -X PUT --data-binary @- api/v1/config # expiry & housekeeping tp --expire 24 # drop anything older than a day tp --expire 0.5 # drop anything older than 30 minutes tp -c # drop everything
Development & CI
The repository ships with a GitHub Actions workflow at .github/workflows/ci.yml that runs cargo check, the full cargo test suite (5,635 tests — 5,082 unit + 553 integration), cargo fmt --check, cargo clippy, cargo doc, and a release build on every push to main and on every pull request. Criterion benchmarks live under benches/benchmarks.rs (cargo bench).
For more detail on architecture, test breakdown, and module-level statistics, see the engineering report.