Watch a file — or the output of any command — and run something every time it changes.
aux4 watch is for reacting to a difference you cannot name in advance. If you already know the value you are waiting for, aux4/poller expresses that directly:
aux4 poll — wait until a deploy reports READY, then continue. You can name the target.aux4 watch — back up the log whenever it grows. You cannot; you only know it will differ.aux4 aux4 pkger install aux4/watch
Back up a log file every time it is written to:
aux4 watch file sys.log --onChange "cp sys.log backup/" --times unlimited
Block until a build artifact shows up, then carry on:
aux4 watch file target/report.csv --maxWait 600
React when someone pushes to a remote branch:
aux4 watch command "git ls-remote origin main" --onChange "git pull" --interval 60 --times unlimited
Watches a single file. The file does not need to exist yet — whatever state is present at startup becomes the baseline, and startup never triggers anything, so pointing a watch at a missing file makes it fire when the file appears.
aux4 watch file <path> [--onChange <command>] [--onDelete <command>] [--onCreate <command>] [--mode <mtime|content>] [--interval <seconds>] [--debounce <milliseconds>] [--times <count|unlimited>] [--maxWait <seconds>]
| Option | Description | Default | | --- | --- | --- | | path | File to watch, as a positional argument | required | | --onChange | Runs when the file changes or is created | | | --onDelete | Runs when the file is deleted. Never runs onChange | | | --onCreate | Runs when the file is created. Falls back to onChange | | | --mode | mtime (modification time and size) or content (sha256) | mtime | | --interval | Seconds between fallback polls | 2 | | --debounce | Milliseconds of quiet required before reacting | 200 | | --times | How many changes to react to, or unlimited | 1 | | --maxWait | Seconds to wait for the next change before giving up | waits forever |
Runs a command on an interval and treats its output as a fingerprint. This is the general form — anything you can reduce to a line of output can be watched: HTTP endpoints, git refs, query results, DNS records, package versions.
aux4 watch command <command> [--onChange <command>] [--interval <seconds>] [--times <count|unlimited>] [--maxWait <seconds>]
| Option | Description | Default | | --- | --- | --- | | command | Command whose output is the fingerprint, as a positional argument | required | | --onChange | Runs when the output changes | | | --interval | Seconds between polls | 2 | | --times | How many changes to react to, or unlimited | 1 | | --maxWait | Seconds to wait for the next change before giving up | waits forever |
watch file uses filesystem events for an immediate reaction and keeps polling underneath as a safety net, because events are unreliable or absent on NFS, SMB shares, and some container bind mounts. What it actually watches is the parent directory filtered to the filename, which is what lets a not-yet-created file be watched at all and what keeps a watch alive across a log rotation that replaces the file.
watch command has no events available to it, so --interval is its only clock. Match it to how fast the thing you are watching really moves — a remote git ref does not need checking every two seconds.
mtime compares modification time and size. Size is part of it because modification time has whole-second granularity on some filesystems, so two appends inside the same second would otherwise be invisible. This is the right default for logs.
content compares a sha256 of the file, so a touch that does not change the bytes is not a change. It costs a full read on every check, which is worth it for a config file and wasteful for a large log.
A process writing a file emits a burst of events. --debounce is the quiet period required before reacting, so a burst fires the hook once instead of dozens of times — and a hook that copies the file is far less likely to catch it half-written.
Three transitions are distinguished, and they are not interchangeable:
| Transition | Runs | | --- | --- | | File exists and its fingerprint differs | --onChange | | File appeared where there was none | --onCreate, or --onChange when unset | | File disappeared | --onDelete only |
A deletion never falls through to --onChange, because there is nothing left for the hook to act on — running cp sys.log ... against a missing file would fail by construction.
Only --onChange and --onCreate count towards --times. Deletions do not, so a log rotation does not spend part of the budget on its way to the next real change.
An unlimited watch never returns, so run it under aux4/jobs when it should outlive your shell:
aux4 jobs run --command "aux4 watch file sys.log --onChange 'cp sys.log backup/' --times unlimited"
That also gives you --onFailure for alerting if the watch ever exits.
| Code | Meaning | | --- | --- | | 0 | The --times budget was satisfied | | 124 | No change arrived within --maxWait | | other | The exit code of the hook, passed through unchanged |
124 follows the convention used by timeout, which keeps 1–123 free for your hook's own codes so nothing is ambiguous.
--maxWait bounds the wait for the next change, not the total run. The clock restarts every time a change fires, so --times 10 --maxWait 60 means "give up if a minute passes with nothing happening."
This package is licensed under the Apache License, Version 2.0.
See LICENSE for details.