Bash脚本进程检测异常:实际无进程却误报运行状态
进程检查脚本异常问题排查
你使用以下Bash函数检查进程是否已运行:
func () { local arrayOfParams=$@ local CMD="ps aux | grep -v grep" for item in ${arrayOfParams[@]}; do local CMD="$CMD | grep -e $item" done echo "Current process PID : $$" echo "`date`: CMD is -> $CMD" local isProcRunning=`eval ${CMD}` if [[ 0 -eq `echo "$isProcRunning" | wc -l` ]]; then echo "`date`: Following process is running: " echo "$isProcRunning" echo "" if [[ "$FORCE_RUN" == true ]]; then echo "`date`: User forced the run of current process ..." # 提取符合搜索条件的进程PID PIDS=(`echo $isProcRunning | awk '{print \$2}'`) for PID in "${PIDS[@]}"; do # 获取对应PID的命令,明确要杀死的进程 local PIDCMD=`ps -o cmd -p ${PID} --no-headers` echo "`date`: Killing PID: $PID with CMD -> $PIDCMD" kill -9 $PID done else echo "`date`: Current process will exit!!!" fi exit 0 fi }
实际使用时,你用ps -ef结合指定参数在CMD字段过滤进程,已排除当前PID对应的行,预期wc -l返回0,但脚本内执行时返回1;而在脚本外执行相同命令时,wc -l确实返回0。
问题原因分析
临时grep子进程被ps捕捉:脚本里用
eval执行拼接的ps | grep命令时,整个命令会以子进程组的形式运行。ps执行的瞬间,会把正在运行的grep子进程(就是你用来过滤的那个grep)也包含在进程快照里,这就多出来一行输出,导致wc -l返回1。而手动在终端执行时,命令执行流程更短,ps通常不会捕捉到这个转瞬即逝的grep进程。数组参数处理错误:
local arrayOfParams=$@这行代码有误,$@是保留空格的参数列表,但直接赋值给普通变量会丢失数组结构,把所有参数合并成一个字符串。后续遍历${arrayOfParams[@]}其实是按空格拆分这个字符串,不是遍历原始的参数数组——如果参数里有空格,会导致grep过滤规则混乱,甚至匹配到无关进程。逻辑判断完全写反:你的
if [[ 0 -eqecho "$isProcRunning" | wc -l]]判断逻辑搞反了——当wc -l返回0时,说明没有匹配到任何进程,但你在这个分支里却输出“Following process is running”,这不仅会误导你,还会导致后续的退出/杀进程逻辑完全错误。
修正建议
针对上述问题,调整后的脚本示例如下:
func () { local -a arrayOfParams=("$@") # 正确定义参数数组,保留空格 local CMD="ps aux" # 构建过滤规则,用grep -F避免正则解析问题 local filters="" for item in "${arrayOfParams[@]}"; do filters+=" | grep -F -e '$item'" done # 追加过滤:排除当前脚本PID,同时排除grep自身进程 CMD="$CMD $filters | grep -v grep | grep -v $$" echo "Current process PID : $$" echo "$(date): CMD is -> $CMD" local isProcRunning=$(eval "$CMD") local line_count=$(echo "$isProcRunning" | wc -l) if [[ $line_count -gt 0 ]]; then # 逻辑修正:有匹配进程时进入分支 echo "$(date): Following process is running: " echo "$isProcRunning" echo "" if [[ "$FORCE_RUN" == true ]]; then echo "$(date): User forced the run of current process ..." # 用while循环读取PID,避免数组拆分空格的问题 echo "$isProcRunning" | awk '{print $2}' | while read -r PID; do local PIDCMD=$(ps -o cmd -p "$PID" --no-headers) echo "$(date): Killing PID: $PID with CMD -> $PIDCMD" kill -9 "$PID" done else echo "$(date): Current process will exit!!!" fi exit 0 fi }
内容的提问来源于stack exchange,提问作者Alex R.
相关产品推荐
相关产品推荐

