脚本中grep异常行为排查及进程PID获取优化咨询
ps -ef | grep beam.smp会偶尔抓到grep进程?怎么解决? 这个问题其实是典型的竞态条件导致的,我来给你拆解一下原因,再分享几个更靠谱的解决办法:
问题原因
当你在脚本里执行ps -ef | grep beam.smp | grep -v grep ...这串管道命令时,系统会同时启动所有管道里的进程(ps、第一个grep、第二个grep等),而不是严格按顺序逐个执行。
如果ps -ef命令在第一个grep beam.smp进程还在运行的时候就完成了进程列表的抓取,那么这个grep进程本身(命令行里包含beam.smp)就会被ps列出来,然后被第一个grep匹配到。而后续的grep -v grep虽然会过滤掉包含grep的行,但如果时间差刚好赶上,就可能出现漏过滤的情况——毕竟所有进程是并行跑的,不是串行等待前一个完成再启动下一个。
而你手动在终端执行时,因为输入命令到触发执行有一定延迟,grep进程往往已经快执行完毕了,所以ps很难抓到它,因此很少出现问题。
更优的解决方法
这里有几个比grep -v grep更可靠的方案,按推荐程度排序:
1. 用ps自带的过滤参数(最高效)
直接让ps自己过滤进程,完全不需要管道和grep,从根源避免竞态问题:
ps -C beam.smp -o pid=
解释:
-C beam.smp:指定要匹配的进程命令名(就是你要找的beam.smp)-o pid=:只输出PID列,最后的=是去掉列头,直接得到纯PID值
如果要取第一个PID(万一有多个实例),可以加head -n1:
ps -C beam.smp -o pid= | head -n1
2. 用pgrep(最简洁)
pgrep是专门用来查找进程PID的工具,默认只输出匹配的PID,完全不用自己处理过滤:
pgrep beam.smp
同样,取第一个PID的话:
pgrep beam.smp | head -n1
如果需要更严格的匹配(比如匹配完整命令行而不是仅进程名),可以用pgrep -f beam.smp,不过一般beam.smp作为进程名已经足够了。
3. 修改grep的匹配规则(兼容老系统)
如果你的系统没有pgrep或者不想用ps的特殊参数,可以修改grep的匹配方式,让它不会匹配到自己:
ps -ef | grep [b]eam.smp | awk '{print $2}' | head -n1
解释:这里用正则表达式[b]eam.smp来匹配,ps列出的beam.smp进程会被正常匹配,但grep自己的命令行是grep [b]eam.smp,这个字符串里的[b]和beam.smp里的b不匹配,所以grep不会把自己列出来,也就不需要grep -v grep了。
内容的提问来源于stack exchange,提问作者parasit

