检查特定路径下的程序是否正在运行

6

我正在尝试使用以下代码检查进程是否正在运行:

SERVICE="./yowsup/yowsup-cli"
RESULT=`ps aux | grep $SERVICE`

if [ "${RESULT:-null}" = null ]; then
    echo "not running"
else
    echo "running"
fi

但它一直在回显正在运行,尽管实际上并没有在运行。我意识到grep本身就作为一个结果出现了,这就是问题所在。
如何跳过grep,仅检查进程?

2
通过 pid:https://dev59.com/c3A75IYBdhLWcg3w6Ndj - Alberto Salvia Novella
2
按照名称:https://askubuntu.com/questions/157779/how-to-determine-whether-a-process-is-running-or-not-and-make-use-it-to-make-a-c/1317605 - Alberto Salvia Novella
8个回答

14

使用pgrep命令:

if pgrep "$SERVICE" >/dev/null 2>&1 ; then
    echo "$SERVICE is running"
fi
或者:
if pgrep -f "/path/to/$SERVICE" >/dev/null 2>&1 ; then
    echo "$SERVICE is running"
fi

注意:

  • pgrep 将其参数解释为一个 正则表达式,因此包含正则表达式字符的路径可能无法匹配或产生误报 (例如,pgrep -f /home/user/projects/c++/application/executable由于+, 无法按预期工作)。可以通过转义相关字符来解决此问题 (例如,pgrep -f /home/user/projects/c\+\+/application/executable)。

  • pgrep -f <pattern> 对正在运行的进程的整个命令行匹配指定的模式。因此,它将匹配出现为其他进程参数的路径 (例如,在一个终端中运行 nano /usr/bin/sleep 并在另一个终端中运行 pgrep -f /usr/bin/sleep -> pgrep 报告 nano 的 pid,因为它的命令行中包含/usr/bin/sleep作为参数)。为防止这些误报,请在模式前加上脱字符 (^),以强制 pgrep 只匹配命令行的开头 (例如,pgrep -f ^/usr/bin/sleep)。


实际上,pgrep解释指定的参数作为“模式”,因此它并不是那么可靠。结果,包含正则表达式字符的路径将无法匹配或产生误报,例如,pgrep -f "/home/user/projects/c++/application/executable"的效果与预期不符。 - Fonic
@Maxxim 很好的观点,感谢您的建议!... 我知道 grep-F 选项用于固定字符串搜索(无模式),但是 pgrep 显然没有这个选项。 - hek2mgl
ps: awk 可用于固定字符串搜索。像这样:ps ax -o pid,cmd | awk -v search='search_string' '{pid=$1;$1=""} index($0, search){print pid}' ... 在此示例中,search_string 可包含任何类型的特殊字符。 - hek2mgl
经过进一步检查,我认为 pgrep -f 实际上并不可靠,最好完全避免使用。请尝试以下操作:在一个终端中运行 nano /usr/bin/sleep,在另一个终端中运行 pgrep -f /usr/bin/sleep -> pgrep 将报告 nano 的 pid,因为它的命令行中包含 /usr/bin/sleep。看起来 pgrep -f 盲目地匹配整个命令行,包括参数。我认为这使得 pgrep -f 完全不适用于此用例。 - Fonic
@Maxxim,看来你是对的,我应该从我的答案中删除-f。如果你想要的话,我会把这个留给你?与此同时,你的注释与答案保持同步。再次感谢,非常感谢! - hek2mgl
我选择再添加一条注释,因为有一个可行的解决方法。并且,我认为最好保留一个(部分)错误的答案并解释其缺陷/限制,以便其他人能从中学习。我猜测,如果您将其删除,其他人可能会重新发布它,因为“pgrep”的失误并不是很明显(我花了相当长的时间才弄清楚)。虽然我们现在更了解它,但我去掉了“更可靠”的说法。 - Fonic

7

对于没有pgrep命令的系统,您可以使用:

service="[.]/yowsup/yowsup-cli"

if ps aux | grep -q "$service"; then
    echo "not running"
else
    echo "running"
fi
  • [.]会强制grep不将自己列出,因为它不符合[.]的正则表达式。
  • 可以使用grep -q来避免命令替换步骤。
  • 在shell中最好使用小写变量。

4
问题在于,有时候你调用grep命令会出现在ps列表中,因此只有在交互式检查时才能正常工作。
$ ps -ef | grep bash
...
myaut    19193  2332  0 17:28 pts/11   00:00:00 /bin/bash
myaut    19853 15963  0 19:10 pts/6    00:00:00 grep --color=auto bash

最简单的方法是使用pidof。它接受完整路径和可执行文件名:

service="./yowsup/yowsup-cli" # or service="yowsup-cli"
if pidof "$service" >/dev/null; then
    echo "not running"
else
    echo "running"
fi

有一种更强大的pidof版本-- pgrep


然而,如果您从一个脚本启动程序,则可以将其PID保存到文件中:

service="./yowsup/yowsup-cli"
pidfile="./yowsup/yowsup-cli.pid"
service &
pid=$!
echo $pid > $pidfile

然后使用pgrep命令检查它:

if pgrep -F "$pidfile" >/dev/null; then
    echo "not running"
else
    echo "running"
fi

这是/etc/init.d启动脚本中常见的技术。

2
以下解决方案避免了使用 ps + greppgreppidof 出现的问题(见下文的 优点):
# Check if process is running [$1: path to executable]
function is_process_running() {
    local path="$1" line
    while read -r line; do
        [[ "${line}" == "${path}" || "${line}" == "${path} "* ]] && return 0
    done < <(ps -e -o command=)
    return 1
}

is_process_running "./yowsup/yowsup-cli" && echo "running" || echo "not running"

解释:

  • ps -e -o command=列出所有进程,仅输出每个进程的命令行,省略标题行
  • while read -r line; do ... done < <(ps ...)逐行处理由ps生成的输出
  • [[ "${line}" == "${path}" || "${line}" == "${path} "* ]]检查行是否完全匹配路径或路径+空格+参数

优势:

  • 适用于包含正则表达式特殊字符的路径,这些字符在没有选项-Fpgrep的情况下会使grep报错,例如:/home/user/projects/c++/application/executable(有关详细信息,请参见此答案中的注意事项
  • 避免了使用ps+ grep / pgrep时报告路径作为其他进程的某些参数而导致误报的问题(例如:nano /usr/bin/sleep+pgrep -f /usr/bin/sleep->错误地报告nano进程的pid)
  • 避免了pidof对从PATH 运行的进程误报的问题(例如:sleep 60s &+pidof /tmp/sleep->错误地报告从/usr/bin/sleep运行的sleep进程的pid,而不管/tmp/sleep是否实际存在)

1
## bash

## function to check if a process is alive and running:

_isRunning() {
    ps -o comm= -C "$1" 2>/dev/null | grep -x "$1" >/dev/null 2>&1
}

## example 1: checking if "gedit" is running

if _isRunning gedit; then
    echo "gedit is running"
else
    echo "gedit is not running"
fi

## example 2: start lxpanel if it is not there

if ! _isRunning lxpanel; then
    lxpanel &
fi

## or

_isRunning lxpanel || (lxpanel &)

注意:即使lxpanel已经变成僵尸进程(defunct),使用"pgrep -x lxpanel"或"pidof lxpanel"仍会报告lxpanel正在运行;因此,为了获取正在运行的进程,我们需要使用"ps"和"grep"。

1
我认为 pidof 是为了这个而制作的。
function isrunning()
{
    pidof -s "$1" > /dev/null 2>&1
    status=$?
    if [[ "$status" -eq 0 ]]; then
        echo 1
    else
        echo 0
    fi
)
if [[ $(isrunning bash) -eq 1 ]]; then echo "bash is running"; fi
if [[ $(isrunning foo) -eq 1 ]]; then echo "foo is running"; fi

0
current_pid="$$" # get current pid
# Looking for current pid. Don't save lines either grep or current_pid
isRunning=$(ps -fea | grep -i $current_pid | grep -v -e grep -e $current_pid)

# Check if this script is running
if [[ -n "$isRunning" ]]; then
    echo "This script is already running."
fi

0
SERVICE="./yowsup/yowsup-cli"
RESULT=`ps aux | grep $SERVICE|grep -v grep`

if [ "${RESULT:-null}" = null ]; then
    echo "not running"
else
    echo "running"
fi

1
请分享更多细节,以便其他人可以从中学习。 - Nico Haase
社区鼓励在代码旁边添加解释,而不是仅仅提供基于代码的答案(请参见此处)。 - costaparas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接