opscape

Processes and jobs

watch

Rerun a command every few seconds and show its output full-screen, optionally highlighting changes or exiting when they happen.

watch [OPTION...] COMMAND

6 of 7 examples run in Debian 13.7 on 2026-10-31 · procps 2:4.0.4-9 · manual: watch(1)

watch 'ls -l /var/backups' clears the terminal, runs the command, shows its output under a header line (Every 2.0s: ls -l /var/backups host: date), and repeats every two seconds until you press Ctrl-C. It turns any one-shot command into a live dashboard: a file growing during a download, a queue draining, df -h while a disk fills, ss -tn during a load test. -n sets the interval, -d highlights what changed since the last run (-d=permanent keeps everything that ever changed highlighted), -t hides the header.

The command is passed to sh -c, so pipes and quotes work if you quote the whole thing: watch 'ps aux | grep [n]ginx'. Without the quotes, your own shell takes the | and pipes watch's screen output into grep.

watch needs a terminal to draw on. The examples run without one, where its screen output is unreadable, so they discard it and use the options that make watch useful in scripts too: -g exits when the output changes, -e when the command fails, and -q when the output stops changing. The full-screen display itself is described in the first example, which cannot run here.

Examples

Rerun a command every two seconds

watch -n 2 -d 'ls -l logs'
explain

Not run in the sandbox: needs an interactive terminal for its full-screen display, and runs until Ctrl-C. No output is shown because none was captured.

Redraws the directory listing every 2 seconds, with -d highlighting characters that changed since the previous run, such as a growing file size. The header shows the interval, the command, the host name and the time. Press Ctrl-C to leave.

Wait until a command's output changes

(sleep 1; touch new-report.csv) &
watch -g -n 0.2 ls > /dev/null
echo "watch exit status: $?, the directory changed:"
ls new-report.csv
output
watch exit status: 0, the directory changed:
new-report.csv

-g (--chgexit) makes watch exit as soon as the output differs from the previous run, turning it into a "block until something happens" tool. Here a background job creates a file after a second. The screen output is discarded because there is no terminal.

Stop when the command fails

(sleep 1; touch stop.flag) &
watch -n 0.2 -e 'test ! -e stop.flag' > /dev/null
echo "watch exit status: $?"
output
watch exit status: 8

-e (--errexit) stops when the command returns a non-zero status, here once stop.flag exists. In a terminal, watch then waits for a key press; the exit status is 8.

Wait until output stops changing

(for i in 1 2 3; do echo "chunk $i" >> download.part; sleep 0.3; done) &
watch -q 3 -n 0.3 'wc -c < download.part' > /dev/null
echo "size stable at $(wc -c < download.part) bytes"
output
size stable at 24 bytes

-q N (--equexit, procps 4) exits once the output has stayed the same for N runs. The background loop stands in for a download; when it stops writing, the size stays put for three intervals and watch returns.

Wait for a server to start listening

(sleep 1; exec python3 -m http.server 9000 > /dev/null 2>&1) &
watch -g -n 0.3 'ss -ltn | grep -c :9000' > /dev/null
ss -ltn | grep :9000
output
LISTEN    0         5                  0.0.0.0:9000             0.0.0.0:*       

The count of listening sockets on port 9000 changes from 0 to 1 when the server comes up, and -g returns at that moment, so the next command can safely connect.

Give up if the change never comes

timeout 2 watch -g -n 0.5 'ls *.done' > /dev/null
echo "exit status: $?"
output
exit status: 124

Nothing creates a .done file, so watch -g would wait forever. Wrapped in timeout, it is stopped after two seconds and the status 124 tells the script the wait timed out.

The script-friendly alternative, a loop

for i in 1 2 3; do
  printf '%s  %s lines\n' "$(date +%T)" "$(wc -l < logs/app.log)"
  sleep 1
done
output
20:22:03  12 lines
20:22:04  12 lines
20:22:05  12 lines

For logs or anything without a terminal, print one timestamped line per iteration instead of redrawing a screen. while sleep 1; do ...; done is the endless version.

Common pitfalls

  • Not quoting a pipeline: watch ls | wc -l pipes watch's screen into wc. Quote the whole command: watch 'ls | wc -l'.

  • Shell aliases and functions are not available, because the command runs in a fresh sh -c. watch ll fails; write watch ls -l.

  • Using double quotes for commands containing $: watch "echo $RANDOM" expands once, before watch starts, and shows the same number forever. Use single quotes so sh -c expands it each time.

  • Wrapping watch in a script or cron job for its display. Without a terminal it writes screen-control output; use -g, -e, -q with the output discarded, or a loop.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • watch comes from procps and is not installed on macOS; brew install watch provides it, or use while :; do clear; cmd; sleep 2; done. FreeBSD has an unrelated watch that snoops on terminal sessions.

  • -q/--equexit is new in procps 4 and missing from older Linux distributions.

Options worth knowing

A selection, described in our own words. For the full list, see watch(1) or watch --help.

OptionWhat it does
-n, --interval SECONDSRun the command every SECONDS (default 2, fractions allowed).
-d, --differences[=permanent]Highlight changes since the last run; =permanent keeps all changes highlighted.
-t, --no-titleHide the header line.
-g, --chgexitExit when the command's output changes.
-e, --errexitStop when the command exits with a non-zero status.
-q, --equexit CYCLESExit when the output has not changed for CYCLES runs.
-b, --beepBeep when the command exits with a non-zero status.
-c, --colorInterpret ANSI color sequences in the output.
-C, --no-colorDo not interpret ANSI color sequences.
-p, --preciseStart runs at exact intervals, not interval after each run ends.
-w, --no-wrapTruncate long lines instead of wrapping them.
-x, --execRun the command directly instead of through sh -c.
-r, --no-rerunDo not rerun the command when the window is resized.

Related