为何Bash中两种后台执行方式的$!返回PID不同?
两种后台执行脚本命令的差异及$!返回PID不同的原因
一、两个命令的执行逻辑区别
1. 子shell括号语法:(cd /path && ./script.sh) &
括号会触发bash创建一个子shell进程(新的bash实例),这个子shell先执行cd命令,再启动test_link.sh脚本。由于脚本是外部命令,子shell会额外fork一个新进程来运行它——最终test_link.sh是子shell的子进程,$!记录的是后台启动的子shell PID,而非脚本本身的PID。
2. bash -c语法:bash -c "cd /path && ./script.sh" &
bash -c会启动一个新bash进程执行引号内的命令序列。当执行到序列最后一条命令(./test_link.sh)时,bash会调用exec系统调用把自身进程直接替换成脚本进程,不会额外fork新进程。因此最终运行脚本的就是bash -c启动的进程,$!直接返回脚本的PID。
二、$!返回不同PID的核心原因
- 子shell(括号语法)会保留自身进程,每执行一个外部命令就fork子进程,脚本是子shell的后代进程。
bash -c处理命令序列的最后一条外部命令时,会用exec替换自身,避免冗余进程,脚本进程就是bash -c启动的进程本身。
三、解决5小时后杀死脚本的可行方案
方案1:子shell方式下记录脚本PID
执行脚本时直接把它的PID写入临时文件:
(cd /mnt/sda/tcpdump_monitor && ./test_link.sh & echo $! > /tmp/test_link.pid) &
然后给at命令指定杀死这个PID:
echo "kill $(cat /tmp/test_link.pid)" | at +5 hours
方案2:改用bash -c语法
直接用bash -c方式执行,此时$!就是脚本PID,直接使用即可:
bash -c "cd /mnt/sda/tcpdump_monitor && ./test_link.sh" & echo "kill $!" | at +5 hours
方案3:按进程名杀死(需注意重名风险)
如果脚本名称唯一,可以直接匹配进程名:
echo "pkill -f test_link.sh" | at +5 hours
内容的提问来源于stack exchange,提问作者liang cheng
相关产品推荐
相关产品推荐

