targets 'make 2>&1 | tee build.log' → prints build.log. That's the whole
pitch: give bash-write-targets a bash command string, get back the list
of files it may write to — before anything runs.
pip install bash-write-targets
$ targets 'grep -r foo . | sort -o sorted.txt; mv sorted.txt final.txt'
sorted.txt
final.txt
$ targets --json 'sed -i "s/a/b/g" config.ini && curl -s -o app.tgz https://example.com/app'
["config.ini", "app.tgz"]
My agent-guard project
ships a Bash PreToolUse hook that audits commands before an AI agent runs
them. Its README has an Honest limitations section, and one bullet reads:
Bash write-target extraction is static and approximate. ... This is the documented frontier for a future version, not a finished parser.
So I built the future version — as a standalone, stdlib-only tool anyone can
use, hook or no hook.
What it catches: > >> &> 2>, heredocs (bodies are stripped so
<<EOF content isn't mistaken for targets), $(...) and backticks
(scanned recursively), tee (all operands, -a aware), cp/ mv/ install
destinations, sed -i (script vs. file disambiguation), dd of=,
truncate, touch, zip archives, and -o/--output flags for curl,
wget, gcc, sort, pandoc, psql, tar -f.
What it deliberately skips instead of guessing: /dev/null, fd
duplications (2>&1), unresolvable $VAR s, bare globs.
Pure static parsing over the command string. Nothing is executed, no
subshell is spawned, no file is touched — auditing a destructive command
cannot itself be destructive. And it's conservative by design: a missed
target is a miss, never a crash. Malformed input yields fewer results, not
an exception.
eval, paths assembled at runtime, and python3 -c "open(p).write(x)"
are known gaps. Network sinks (curl --data POSTing somewhere) are out of
scope — this lists file write targets. 19/19 smoke tests green.
pip install bash-write-targets (