SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor

Linux watch 命令详解:间隔、差异高亮与管道

掌握 Linux watch 命令:设置合理刷新间隔、用 -d 高亮差异、变化后退出,并正确引用管道,避免 watch 只执行部分命令。

watch 命令的作用

watch 命令按固定间隔重复运行另一个命令,并在屏幕上的同一位置重新绘制其输出。默认间隔为 2 秒。当您需要等待某个数值变化时,可以使用它,例如等待磁盘空间逐渐占满、容器完成稳定、证书续期完成或队列逐渐清空。

watch 来自 procps-ng。在全新安装的 Ubuntu 或 Debian VPS 上,它已作为 procps 软件包的一部分预装。在使用下面的任何选项前,请先检查版本,因为其中两个实用选项是近期才加入的。

watch --version

在 Ubuntu 24.04 上,该命令会输出类似 watch from procps-ng 4.0.4 的一行内容。下面是最简单的实用示例。

watch df -h /

屏幕会清空,并显示一行标题、一个空行,然后显示 df -h / 的输出:

Every 2.0s: df -h /                          server1: Sun Aug  9 10:21:44 2026

左侧显示间隔和 watch 正在运行的完整命令。右侧显示主机名和当前时间。时钟很有用,因为即使下面的数值没有变化,它也能证明屏幕仍在刷新。按 Ctrl+C 退出。

watch 会绘制适合终端大小的一帧内容。长行会自动换行;-w 则会截断长行。超出底部行的内容不会绘制。帧内没有滚动历史,因为每个周期都会覆盖上一帧。这个特性决定了 watch 的大多数适用场景。

watch 应多久重新运行一次命令?

2 秒只是默认值。应根据数值变化速度和每次运行的开销选择间隔。

  • 对持续变化的计数器使用 -n 0.5,例如 ss -s 或接口字节计数。watch 接受的最小间隔为 0.1 秒。
  • 对磁盘和证书使用 -n 30-n 60。一天才写满的文件系统无需每分钟刷新三十次。

watch 会运行命令,等待命令完成,然后才按指定间隔休眠。命令运行时间会计入间隔,因此在 -n 2 下运行耗时 8 秒的命令会每 10 秒生成一帧,并使计算机始终处于繁忙状态。添加 -p--precise)后,watch 会改为尝试每隔指定秒数启动一次命令;间隔从上一次运行开始到下一次运行开始计算。

如果始终需要使用其他默认值,请在 shell 配置文件中导出 WATCH_INTERVAL。显式指定 -n 仍会覆盖该变量。运行 watch --help,确认你的构建版本会读取该变量。

使用 -d 突出显示变化

从一整屏相同文本中找出移动的字段并不容易。-d--differences)会自动完成这项工作。

watch -n 30 -d 'df -h / /var'

与上一帧不同的字符会以反白显示,因此 Use% 单元格一移动就会立即亮起,其他内容保持不变。如果 /var 不是独立文件系统,您会看到根文件系统列出两次。这是快速了解系统磁盘分区方式的方法。

比较按位置进行,watch 会将新帧与旧帧逐字符对齐。列宽发生变化的输出几乎会全部亮起,因此应优先使用布局稳定的命令。当前版本的 procps-ng 也接受 watch --differences=permanent,该选项会保留所有曾经发生变化的位置,而不只是突出显示最近一次变化。依赖此选项前,请使用 watch --help 确认其可用性。

如何在输出发生变化时退出 watch?

-g--chgexit)会在输出首次与上一次运行不同时退出 watch。命令提示符会返回,因此您可以在同一行的后面继续输入内容。

watch -n 10 -g 'systemctl is-active myapp' ; echo 'state changed'

此选项可将 watch 变成“部署完成后通知我”的工具。但有一个容易踩到的陷阱:比较范围包括完整输出,因此其中的时钟或 PID(进程标识符)会在第一次刷新时发生变化,导致 watch 立即退出。systemctl status myapp会输出经过时间和内存使用量,因此对它使用 -g 没有意义。请将命令缩减为您要等待的唯一稳定信息,这正是 systemctl is-active 提供的内容。

较新的 procps-ng 增加了相反的测试 --equexit <cycles>:当输出连续指定次数保持不变后退出。它用于回答“等它不再变化”的需求。请检查 watch --help,因为较旧的构建版本不支持该选项。

为什么 watch 会忽略管道和 glob?

这是每个人都会遇到一次的问题,原因在 shell 中,而不在 watch 中。

watch docker compose ps | grep web

交互式 shell 会先读取整行,然后在执行任何命令前按管道符拆分这行。它启动 watch docker compose ps,并将 watch 自身的屏幕输出 连接到 grep。watch 不再向终端写入内容,因此显示会损坏或为空,而 grep 过滤的是重绘输出,而不是容器列表。

将整个管道加引号,使其作为一个参数传入。

watch 'docker compose ps | grep web'

watch 会将该字符串传给 sh -c,再由该 shell 在每个周期执行一次管道。glob 也遵循相同规则,但表现得不那么明显。

watch ls -l /var/log/*.log
watch 'ls -l /var/log/*.log'

第一行会在您按下 Enter 时由 shell 执行一次 glob 展开。之后 watch 会反复使用固定的文件名列表,因此一分钟后创建的日志文件不会出现。第二行会将 glob 传给 watch,由 sh 在每个周期内展开,因此新文件会自动显示。

引号还决定变量或命令替换何时求值。watch "echo $(date)" 会在 shell 中执行 date 一次,然后永久回显固定字符串。watch 'echo $(date)' 会在每个周期执行 date。单引号表示稍后求值。双引号表示立即求值。

sh -c 还会带来两个后果:

  • 别名和 shell 函数在其中不存在。watch ll 会因 sh: 1: ll: not found 而失败,因为 sh -c 不会读取您的 .bashrc。请直接写出实际命令,或为仅位于交互式 PATH 中的二进制文件提供完整路径。
  • 建议捕获标准错误。需要让错误文本显示在当前帧中,而不是散落在帧外时,请将 2>&1 放在引号内。

引号难以处理时,-x--exec)会直接运行命令,而不是通过 sh -c 运行。带空格的参数更容易处理,但管道和 glob 会完全失效,因为没有 shell 负责解释它们。

权限遵循相同逻辑,因为 watch 是普通进程。watch 'sudo ss -tulpn' 会触发密码提示,但您无法看到或输入该提示。请改为运行 sudo watch 'ss -tulpn',并记住:在您按下 Ctrl+C 前,整个循环都会以 root 身份运行。

值得在 VPS 上监控的四项内容

正在填满的磁盘

watch -n 30 -d 'df -h / /var'

df 只向内核请求已有的数据,因此可以持续重复执行,开销很低。du -sh /var/log 则不同,因为它每次循环都会遍历目录树中的所有文件。在 watch 下运行它会持续读取磁盘,并拖慢正在诊断的机器。执行一次 du,或使用 ncdu,然后让 df 在 watch 下运行,以判断这个数值是否仍在增长。

不断重启的容器

cd /srv/myapp
watch -n 5 -d 'docker compose ps'

状态列会显示每个容器已运行的时间。发生崩溃循环的容器会不断重置为 “Up 2 seconds”,而 -d 可以让这一点一目了然。先切换目录,因为 watch 会继承启动它的 shell 的工作目录,而 docker compose 需要在项目目录中运行。watch 会告诉您正在发生重启。docker compose logs -f web 会告诉您重启原因。确定原因后,将解决方案写入会自动重启容器的 Compose 健康检查,这样就不必有人一直盯着屏幕。

即将完成的证书续期

sudo -i
watch -n 60 -g 'openssl x509 -noout -enddate -in /etc/letsencrypt/live/example.com/fullchain.pem'

该命令只输出一行,即 notAfter=Nov 7 09:14:22 2026 GMT,不会输出其他内容,因此 -g 只有在文件被续期后的新文件替换时才会触发。/etc/letsencrypt/live 下的文件只有 root 可读,因此该命令需要在 root shell 中运行。请注意,它监控的是磁盘上的证书。运行中的 Web 服务器会将旧证书保留在内存中,直到重新加载,因此请使用 echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -enddate 检查实际对外提供的证书,不要以两秒的间隔对公网主机执行该命令。续期本身由systemd 服务和计时器负责,systemctl list-timers 会显示下一次运行时间。

正在排空的队列

watch -n 15 'postqueue -p | tail -n 1'

该命令会输出一行摘要,例如 -- 24 Kbytes in 6 Requests.;完成后则会输出 Mail queue is empty。同样的形式适用于任何可以计数的队列:watch -n 5 'find /srv/queue/incoming -type f | wc -l'

watch 在这里就达到限制了。它会将当前帧与上一帧进行比较,但无法测试条件,因此永远不能告诉您“计数已达到零”。普通的 shell 循环可以做到这一点,并使用命令替换捕获每次循环中的计数:

until [ "$(find /srv/queue/incoming -type f | wc -l)" -eq 0 ]; do sleep 5; done; echo 'queue drained'

watch 命令不适用的场景

watch 会重复执行命令,但不会记住上一次运行的结果,也不了解该命令的作用。对于 df,这没有问题。但在以下四种情况下不应使用它。

  • 日志。 watch 会重绘固定内容,因此两个周期之间出现并滚动消失的日志行会丢失。应使用 journalctl -fu nginxtail -f,它们会在新行写入时持续输出。
  • 开销较高的命令。 du -sh / 或针对繁忙数据库的查询,无论间隔设置多长,都会变成持续的后台负载,因为 watch 会不断重新启动该命令。
  • 任何具有副作用的操作。 会写入数据、发送请求、重启服务或安装软件的命令,会在每个周期重复执行。每两秒对 API 执行一次 curl,一天就是 43,200 个请求,这可能导致免费 API 密钥被暂停。
  • 需要长期运行的检查。 watch 在您的终端中运行,会随会话结束而退出。它不会发送告警;关闭笔记本电脑后也不会保留历史记录。此时应使用真正的状态监控工具,例如 Uptime Kuma,或在服务器上运行的定时检查。

明确的界限在于持续时间。watch 适合您等待某件特定事情发生时使用的十分钟。任何明天仍应继续执行的检查,都应交给定时器或真正的监控系统。

通过 SSH 在 tmux 中运行 watch

通过 SSH 使用时,watch 的持久性取决于连接。连接中断后,shell 会收到 SIGHUP,并连带终止 watch。tmux 可以避免这种情况,因此 watch 在远程服务器上才真正实用。

tmux new -As ops
watch -n 30 -d 'df -h /'

按 Ctrl+b,然后按 d,分离 tmux 会话。循环会继续在服务器上运行。稍后使用 tmux attach -t ops 重新连接,画面仍会持续更新,顶部时钟也会证明这一点。拆分窗口后,可以在一侧运行 watch,另一侧运行 journalctl -f。这正是基于 tmux 的终端工作台的用途。如果同一个问题同时适用于多台服务器,应使用用于管理多台 Linux 服务器的工具,而不是铺满 watch 窗格。

这里引用的屏幕内容仅作示例。不同发行版和 procps-ng 版本的列布局及版本字符串可能不同,因此请在自己的计算机上阅读 man watch,确认实际构建版本支持哪些选项。

FAQ

Why does watch ignore the pipe in watch mycmd | grep foo?

Your shell splits the line at the pipe before watch ever starts, so it pipes watch's own screen drawing into grep, and the command's output is never filtered. Quote the whole pipeline instead: watch 'mycmd | grep foo'. watch hands that single argument to sh -c, which runs the pipeline once per cycle. The same rule applies to globs and to command substitution.

What interval should I use with watch?

Match it to how fast the value moves and how expensive one run is. Half a second suits fast counters, and 0.1 seconds is the smallest interval watch accepts. Thirty to sixty seconds suits disks and certificates. The command's runtime is added to the gap unless you pass -p, so a slow command under -n 1 simply runs back to back and never pauses.

Can watch follow a log file?

No. watch redraws one frame and keeps no history, so any line that appears and scrolls away between two cycles is gone for good. journalctl -fu <unit> and tail -f stream new lines as they arrive, which is what logs need. Keep watch for values that have a current state, such as a disk percentage or a queue length.

How do I make watch stop when the thing I am waiting for happens?

Use -g (--chgexit) and give it a command whose output stays identical until that event. Output containing a clock or a PID changes on the first refresh and exits immediately, so systemctl is-active myapp works where systemctl status myapp does not. For a real condition such as a count reaching zero, use a shell until loop, because watch can only detect that the output changed.

Why does watch say command not found for something that works in my shell?

watch passes the command to sh -c, and that shell does not read .bashrc, so your aliases and shell functions do not exist there. The message is usually sh: 1: ll: not found. Write the real command out, or use the full path for a binary that only sits on your interactive PATH.