在/bin/bash -c中使用$$与$!的区别是什么?附测试场景疑问
关于
/bin/bash -c中$$与$!的区别及你的命令分析 先把最核心的区别说清楚,这俩变量的含义完全不同:
$$:代表当前执行这段代码的shell进程自身的PID。不管你是在交互式终端里敲命令,还是用bash -c启动一个一次性子shell,$$永远指向正在解析执行这个变量的那个shell进程的ID。$!:代表当前shell最近一次启动的后台子进程的PID。注意是「当前shell」的后台任务,它是父shell用来记录子进程ID的变量。
接下来结合你提到的几个场景逐一拆解:
1. 先看你提到的那条单行命令:/bin/sh -c 'echo $$>/tmp/my.pid && exec program args' &
这条命令的巧妙之处在于用exec替换了子shell进程:
- 首先
/bin/sh -c启动一个子shell,此时$$就是这个子shell的PID; - 然后用
exec program args直接把这个子shell的进程空间替换成program的进程,所以最终program的PID和之前的$$完全一致; - 这样写入
/tmp/my.pid的就是真正运行的程序的PID,后续可以直接用这个PID来管理程序(比如kill),这也是这条命令的设计目的。
2. 你遇到的几个变体场景解析
- 内部和外部的
$$对应不同PID:比如你在交互式shell里运行echo $$; bash -c 'echo $$',第一个$$是你当前终端的shell进程ID,第二个$$是bash -c启动的子shell的ID——因为它们是两个完全独立的shell进程,所以PID自然不同,这是正常现象。 - 后台运行时PID相同:比如
bash -c 'echo $$' &,这里的$$是后台运行的子shell自身的PID,而你在前台用echo $!会得到同一个数值——这是因为$!记录的就是父shell刚刚启动的这个后台子shell的PID,数值相同但含义不同:一个是子shell自己的ID,一个是父shell记录的子shell的ID。 $$与$!对应不同PID:比如运行bash -c 'sleep 10 & echo $$; echo $!',这里$$是子shell的PID,$!是子shell内部启动的sleep后台进程的PID——一个是shell本身,一个是shell spawn出来的子任务,所以PID肯定不一样。
3. 关于kill命令未生效的问题
你运行/bin/bash -c 'echo $$ && (sleep 15 ; echo done)'后,kill输出的PID却还是会出现done,原因是:(sleep 15; echo done)是在子shell里运行的——外层的bash -c进程fork出了一个子shell来执行括号里的内容,这个子shell和父shell是独立的进程。当你kill外层的bash -c进程时,子shell已经脱离了父进程的控制(或者说已经启动完成),所以它会继续运行直到sleep结束,然后输出done。
如果要让kill生效,有几个办法:
- 去掉子shell,直接写
/bin/bash -c 'echo $$ && sleep 15 && echo done',这样sleep是外层shell的直接子进程,kill外层shell的PID会终止整个任务; - 用
exec替换子shell:/bin/bash -c 'echo $$ && exec sleep 15 && echo done'(不过exec后echo done不会执行,因为sleep替换了shell进程); - 杀掉整个进程组:用
kill -TERM -<PID>(注意PID前的减号),这样会杀掉这个PID所在的整个进程组,包括子shell。
4. 可获取前台进程PID且PID相同的场景
比如你运行bash -c 'echo $$; exec sleep 15',这里$$是子shell的PID,exec sleep 15直接把这个子shell进程替换成sleep进程,所以sleep的PID和之前的$$完全一致。这时候你在另一个窗口用这个PID杀进程,就能直接终止sleep,因为它们本质上是同一个进程。
内容的提问来源于stack exchange,提问作者gotjosh
相关产品推荐
相关产品推荐

