watch: the poor man's monitoring, in one command
Re-run it until something changes
Everyone has written this loop, in some form:
while true; do clear; df -h /var; sleep 2; doneRe-run a command, clear the screen, wait, repeat. It works, and watch does the same in eleven characters, plus a handful of things the loop cannot do at all.
watch df -h /varWhat you get for free
The default interval is two seconds, and -n changes it:
watch -n 1 df -h /var # every second
watch -n 0.5 'ls -l | wc -l' # twice a secondNote the quotes on the second one. watch passes the command to sh -c, so an unquoted pipe would be interpreted by your shell and only ls -l would be watched. Quote anything with a pipe, a redirection or a semicolon. If you would rather skip the shell entirely, -x execs the command directly.
The header shows the interval, the command and the host, which sounds cosmetic until you have four of these open and no idea which is which. -t removes it when you want the screen for the output.
The flag that changes how you use the tool is -d, which highlights what changed since the previous run:
watch -d -n 1 'ss -tn state established | wc -l'Your eye stops scanning the whole screen and goes to the parts that moved. With -d=permanent, anything that has ever changed stays highlighted, so you can look away and still see what happened.
The part nobody knows: it can stop on its own
watch is usually described as an infinite loop. A shame, because three flags turn it into a condition to wait for.
-g exits as soon as the output changes:
watch -g -n 5 'systemctl is-active myservice'That blocks until the service state moves, then returns. It’s a wait-for-it primitive without a polling script.
-e exits when the command itself fails:
$ timeout 5 watch -n 1 -e false
$ echo $?
1It stopped on the first failure instead of running for the full five seconds. Handy to babysit something that should keep succeeding, and to know the moment it does not.
And -q <cycles> is the mirror image: exit when the output has not changed for that many cycles. Waiting for a queue to stabilise, or a file to stop growing, is a one-liner:
watch -q 3 -n 2 'ls -l big.iso | awk "{print \$5}"'Three identical readings and it returns. I keep meeting scripts that reimplement this with a counter and a comparison.
It really does need a terminal
watch draws with ncurses, so it wants a real terminal and says so when it does not have one:
$ watch -n 1 date
ncurses: cannot initialize terminal type ($TERM="unknown"); exitingThat is not a bug to work around, it’s a design decision: the tool exists to redraw a screen. In a script or a CI job, the loop you were trying to replace is the right answer, or -g/-q if you only need the exit condition and can give it a TERM.
Not on macOS
$ command -v watch
$watch comes from procps-ng, the Linux process tools, version 4.0.4 on my Ubuntu 25.10 box. macOS ships none of that family:
brew install watchWithout Homebrew, the loop from the first paragraph is the fallback, and clear plus sleep gets you most of the way. What you lose is -d, the flag that actually makes watch worth installing.
← → jump to the previous / next post