大家好!我是大聪明-PLUS!
您是否经常在终端中运行某些程序,结果却永远卡住了?或者相反:您需要每秒查看某些内容的变化,但却固执地按下了向上箭头和 Enter 键?又或者,您想在 5 分钟内安排一项任务,但 cron 实在太麻烦了?
对于所有这些场景,Linux 有三个经过验证的实用程序:timeout、watch、at,我们可以使用脚本和黑客手段来解决,但是......我们使用 Unix 方法并非毫无意义,因为 Unix 方法中的一切都已经发明了很长时间。
今天我们将研究如何使用 timeout、watch 和 at 来管理 Linux 中的命令执行时间。
timeout
timeout --- 是 GNU coreutils 软件包的一部分。其目的是运行命令并监控其时间。如果命令超出限制,则会发出信号终止。
默认信号是SIGTERM,重要的是要理解该过程是否有机会正确完成其工作。
`timeout 5s ./my_script.sh`
这意味着:如果my_script.sh5 秒内没有完成,他就会得到SIGTERM。如果超过 5 秒仍未完成,他就会被杀死。但前提是你明确要求这样做。
我如何确定命令终止的原因?
| 返回代码 | 这是什么意思? |
|---|---|
| 124 | 该队因暂停而被淘汰。 |
| 125 | 错误timeout(例如没有命令) |
| 126 | 该命令不可执行 |
| 127 | 未找到命令 |
| 休息 | 这是命令本身的退出代码。 |
`timeout 3s `sleep` `5`
`echo` `$?`
`
假设你有一个守护进程或服务器,而不仅仅是一个脚本,并且你无法强行关闭它。你想先礼貌地敲门(SIGTERM),如果你不开门,你就想破门而入(SIGKILL)。
`timeout `--signal=`SIGTERM `--kill-after=`2s 10s ./long_running_job`
10秒后,发送SIGTERM信号。如果进程没有响应,则在2秒后收到SIGKILL信号。
这个链条绝对必不可少。终止进程只是成功的一半。你还需要给它机会关闭文件、取消套接字订阅等等。
有时,如果命令自行终止,您可能希望timeout避免干扰其退出代码。那么:
`timeout `--preserve-status` 5s ./backup_script.sh`
如果没有此标志,即使脚本以 0 退出,timeout它也可能根据信号返回 124 或 137。
具有超时的安全卷曲:
if` ! timeout 3s `curl` `-s` https://example.com > /dev/null; `then`
`echo` `"Request failed or timed out"`
`fi
如果有的话,为什么还要这么做curl --max-time?因为timeout:
-
通用(不依赖于实用程序)。
-
可以与任何程序一起工作,而不仅仅是网络程序。
-
允许您不仅检测较长的加载时间,还可以检测任何类型的冻结(例如,如果
curl服务器卡在 DNS 上)。
进程树
如果一个命令timeout启动一个进程,并且该进程产生其他进程,那么:
-
默认情况下, timeout****它只会终止第一个进程。
-
所有孩子都会继续活着,除非他们自杀。
例子:
`timeout 3s `bash` `-c` `"sleep 5 & wait"
sleep即使终止后仍会继续存在bash,因为它是一个独立的进程。要正确终止所有进程,请执行以下操作:
选项 1:setsid
`timeout `--foreground` 3s setsid `bash` `-c` `"sleep 5"=
setsid在新的会话组中启动一个进程。timeout然后它可以通过信号终止整个会话组。
选项 2:脚本内部陷阱
如果你控制了脚本代码,你就可以设置一个陷阱:
#!/bin/bash`
trap `"kill 0"` EXIT
`sleep` `5` &
wait`
然后timeout它将终止该脚本,并且该脚本将终止其所有子脚本。
具有超时逻辑和日志记录的示例脚本
#!/bin/bash`
`log_file="/var/log/mytask.log"`
`cmd="./long_task.sh"`
`timeout_sec=30`
`echo` `"[INFO] Running command with timeout ${timeout_sec}s"` | `tee` `-a` `"$log_file"`
`if` timeout `--kill-after=`5s `"$timeout_sec"` `"$cmd"` >> `"$log_file"` `2`>&1; `then`
`echo` `"[INFO] Command finished successfully"` | `tee` `-a` `"$log_file"`
`else`
`status=$?`
`if` [ `"$status"` `-eq` `124` ]; `then`
`echo` `"[ERROR] Command timed out"` | `tee` `-a` `"$log_file"`
`elif` [ `"$status"` `-eq` `137` ]; `then`
`echo` `"[ERROR] Command killed forcefully"` | `tee` `-a` `"$log_file"`
`else`
`echo` `"[ERROR] Command failed with exit code $status"` | `tee` `-a` `"$log_file"`
`fi`
`fi
它安全、可读,并且可以嵌入到 Ansible 或 init 脚本中。
watch
watch --- 就像这样cron。它不会将任务放在后台;它每 N 秒显示一次命令的结果。
`watch `-n` `2` `'dacongming'
-n 2 --- 启动之间的间隔。---'dacongming' 如果有管道或复杂性,则需要用单引号引起来。
例子:
`watch `-n` `1` `'ls -lh /var/log/myapp'
有用的标志
您至少需要学习和记住以下内容:
| 旗帜 | 它起什么作用? |
|---|---|
-n <> |
通话间隔(默认 2 秒) |
-d |
突出显示已更改的行 |
-t |
摘掉帽子(Every 2.0s: ... date/time) |
-x |
迭代之间不清除屏幕(--no-title) |
-e |
watch如果命令失败则退出 |
典型场景分析
监控可用磁盘空间:
`watch `-d` `-n` `5` df `-h` /mnt/data`
如果您要执行几 GB 的卸载、迁移或 ETL,并且不想耗尽磁盘空间,那么这是理想的选择。
查看日志中的最后几行:
`watch `-n` `0`.5 `'tail -n 10 /var/log/syslog'
半秒是实时读取的正常间隔。如果日志速度很快,tail -F这个间隔可能更有用,但watch使用起来也更方便。
我们监控 systemd 服务:
`watch `-n` `1` `'journalctl -u nginx.service -n 10 --no-pager'
--no-pager必需。否则journalctl,它将卡住,等待您按下按键。
例子
`watch `-n` `1` `'ps -eo pid,etime,%cpu,%mem,cmd --sort=-%cpu | head -10'
这将显示按 CPU 使用率排名的前 10 个进程。
突出显示更改:-d
当您监控指标(CPU、IO、网络接口的 tx/rx)时,系统-d只会显示发生变化的行。您无需搜索具体发生了什么变化。
例子:
`watch `-d` `-n` `1` `'netstat -antp | grep ESTABLISHED'
别名和习惯
在~/.bash_aliases:
`alias `wtail="watch -n 1 'tail -n 20'"`
alias `wdf="watch -d -n 2 'df -h'"`
alias `wps="watch -n 1 'ps aux --sort=-%cpu | head -10'"
输入它比每次wps记住排序标志要快得多。ps aux
限制和功能
默认情况下,watch它仅写入标准输出。因此,如果命令崩溃,您可能不会注意到。要查看所有内容:
`watch `-n` `1` `'dacongming 2>&1'
如果您忘记了引号并且命令包含管道,watch它可能会表现异常或根本无法启动。
糟糕:
`watch `-n` `1` tail `-n` `10` /var/log/syslog | `grep` error`
美好的:
`watch `-n` `1` `'tail -n 10 /var/log/syslog | grep error'
让我们继续。
at:将来运行命令
如果cron 要讲规律,那就一劳永逸 at。如果你需要在 5 分钟内运行某个程序,然后就不用管它,那么 这是一个简单、方便、Unix 风格的工具。而且,不用写 hack 代码。sleep 300 && your_command &
at它并非总是开箱即用。因此,在 Debian/Ubuntu 上:
sudo` apt install at
`sudo` systemctl enable `--now` atd`
如果不启用它atd,您可以随意将任务推送到队列中,但没有人会执行它们。
句法
很简单:
echo` `"dacongming"` | at <dacongming>`
或者以交互方式:
`at `10`:30`
并且您可以输入命令(每行一个),并通过 完成Ctrl+D。
使用示例
2分钟后写入文件:
echo` `"echo 'Hello from the future' > ~/future.txt"` | at now `+` `2` minutes`
重新启动服务:
echo` `"sudo systemctl restart nginx"` | at now `+` `10` minutes`
删除临时文件:
echo` `"rm -rf /tmp/my-temp"` | at `03`:00`
向自己发送通知:
echo` `"DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send 'Сделай перерыв!'"` | at now `+` `1` hour`
notify-send需要正确的环境变量,尤其是在桌面环境中。它at以系统用户身份运行,并且没有 GUI 环境------您需要手动传递DISPLAY它们DBUS_SESSION_BUS_ADDRESS。
如何查看队列
`atq`
示例输出:
`1 2025-07-28 12:30 a user
2 2025-07-28 14:00 a user`
在哪里:
-
第一列是任务ID
-
时间------何时完成
-
a- 活跃(尚未实施)
删除任务
`atrm <job_id>`
例子:
`atrm `1
或者:
`atq | `grep` rotate | `awk` `'{print $1}'` | xargs atrm`
删除与命令中的特定单词相关的所有任务(如果保留命名逻辑)。
如何使用
计划夜间重启服务:
echo` `"sudo systemctl restart myservice"` | at `02`:30
`
15分钟后备份当前目录:
echo` `"tar czf ~/backup_$(date +\%Y\%m\%d).tar.gz $(pwd)"` | at now `+` `15` minutes`
替代时间格式
他的理解是这样的at:
| 格式 | 例子 |
|---|---|
now + 1 hour |
从现在开始 |
midnight |
今天 00:00 |
teatime |
今天下午 4:00 |
tomorrow |
明天 00:00 |
12:30 |
今天 12:30 |
08/01/2025 13:00 |
确切的日期和时间 |
如果您需要快速完成任务"推迟命令并忘记"我们使用at。
结果
timeout以及 ------三个简单却实用的实用程序,涵盖各种场景:从终止冻结进程到实时监控和定向任务执行。请在评论区分享你的使用案例watch。at