Bash脚本无限循环被pkill误杀:为何命令行运行与直接执行表现不同?
我尝试用Bash脚本每24小时重启特定Java进程,但遇到以下问题:pkill -f 'java.*JavaProcess'会同时杀死Restarter脚本本身。奇怪的是,直接在终端粘贴整个while true循环执行时,脚本不会自行终止;但通过bash命令运行该脚本就会出现这个问题。
我曾将while循环放入单独的脚本Temp.bash中,在Restart脚本中将gnome-terminal行改为:gnome-terminal --title='Restarter' -- Temp.bash,此方式可正常运行,但我并不偏好拆分文件的方案。希望了解问题原因,并实现将所有逻辑整合到单个文件中的解决方案。
原脚本如下:
#!/usr/bin/env bash gnome-terminal --title='Restarter' -- bash -c \ " while true do ./instanceOfJavaProcess #opens another window ./secondInstanceOfJavaProcess #opens another window sleep 5 echo Restarting! pkill -f 'java.*JavaProcess' done; exec bash "
当你通过bash -c "..."执行循环逻辑时,运行该循环的bash进程,其命令行参数会包含整个脚本内容(其中就有java.*JavaProcess这个字符串)。而pkill -f是根据整个进程命令行进行匹配的,因此会误将这个运行循环的bash进程判定为匹配目标,进而将其杀死。
而直接在终端粘贴循环执行时,当前shell的命令行中不会包含java.*JavaProcess这个匹配串,所以不会触发误杀。
方案1:给pkill添加精准匹配规则
通过反向匹配排除运行循环的bash进程,或者限定匹配以java开头的进程:
# 方式A:仅匹配以java开头的进程,避免匹配bash命令行中的字符串 pkill -f '^java.*JavaProcess' # 方式B:反向排除包含bash -c的进程 pkill -f 'java.*JavaProcess' -v -f 'bash -c'
其中-v参数表示反向匹配,即杀掉不匹配bash -c的目标进程。
方案2:用pgrep+kill组合精准筛选PID
先通过pgrep找到目标Java进程的PID,再用kill终止,避免误杀:
pgrep -f '^java.*JavaProcess' | xargs kill
方案3:给Java进程添加唯一标识(最可靠)
启动Java进程时添加独特的JVM参数,比如:
# 启动Java进程时添加自定义标识 java -Dprocess.tag=TargetJavaProcess -jar YourJavaApp.jar
之后用pkill匹配这个唯一标识:
pkill -f 'process.tag=TargetJavaProcess'
这种方式完全不会误杀其他进程,是最稳妥的方案。
内容的提问来源于stack exchange,提问作者Mariocraft95

