如何检测带参数的Bash脚本是否已运行及pgrep与wc命令计数异常问题排查
你遇到的核心矛盾是:pgrep -f ... | wc -l的统计结果总是比实际运行的脚本进程数多1,而echo $processes | wc -w却能得到正确值,主要原因如下:
1. pgrep会临时匹配自身进程
当你在脚本内部执行pgrep -f时,pgrep进程启动后会扫描当前系统的所有进程,包括它自己。如果你的正则表达式没有精确限制匹配范围(比如你用了+这类正则特殊字符,且没有锚定行首行尾),pgrep会把自己的PID也输出出来。
虽然这个PID在pgrep退出后立刻消失,但wc -l会统计所有输出的行数(包括这个临时PID的行),导致结果多1:
- 首次运行时,实际只有1个脚本进程,pgrep输出脚本PID+自身PID,
wc -l返回2 - 第二次运行时,实际有2个脚本进程,pgrep输出两个脚本PID+自身PID,
wc -l返回3
你看不到pgrep自身PID的原因是:pgrep输出自己的PID后立刻退出,这个PID对应的进程很快被系统回收,终端输出时可能被缓冲覆盖或未显示。
2. echo $processes | wc -w计数正确的原因
你脚本里的$(pgrep -f "$0 +$*$")存在语法错误(缺少闭合的)),导致processes变量没有正确捕获到pgrep的全部输出(只保留了脚本的PID),因此wc -w统计到的是正确的进程数量。即使语法正确,当你用$(pgrep ...)捕获输出时,pgrep已经退出,它的PID会被系统回收,bash展开变量时可能会忽略无效PID(或换行被替换为空格,临时PID的行被合并后过滤)。
解决方法:准确统计带参数的脚本进程
要避免pgrep匹配自身,同时精确区分带不同参数的脚本进程,你可以采用以下改进方案:
方法1:精确锚定正则表达式
用^和$锚定正则表达式,确保只匹配完全以脚本名+参数开头和结尾的命令行,这样pgrep自己的进程(以pgrep开头)不会被匹配:
#!/bin/bash script_full_cmd="$0 $*" echo "inside test.sh, script command: $script_full_cmd" echo "精确匹配的pgrep结果:" pgrep -f "^$script_full_cmd$" echo "统计进程数量:" count=$(pgrep -f "^$script_full_cmd$" | wc -l) echo $count sleep 300
方法2:排除当前脚本的子进程
用$$(当前脚本的PID)过滤掉pgrep这类子进程:
count=$(pgrep -f "$0 $*" | grep -v $$ | wc -l)
方法3:用ps命令精确匹配
通过ps结合grep过滤,避免匹配自身进程:
count=$(ps aux | grep -E "^[^ ]+ +[^ ]+ +.+$0 $*$" | grep -v grep | wc -l)
内容的提问来源于stack exchange,提问作者Frédéric Charrier

