如何查看进程id (PID)是否存在

在bash脚本中,我想做以下事情(在伪代码中):

if [ a process exists with $PID ]; then


kill $PID


fi

条件语句的合适表达式是什么?

357884 次浏览

我认为这是一个糟糕的解决方案,这为比赛条件打开了大门。如果进程在测试和终止调用之间停止了,该怎么办?那么杀戮就会失败。那么,为什么不在所有情况下都尝试kill,并检查它的返回值以了解它是如何运行的呢?

ps命令和-p $PID可以做到这一点:

$ ps -p 3531
PID TTY          TIME CMD
3531 ?        00:03:07 emacs

要检查进程是否存在,请使用

kill -0 $pid

但就像@unwind说一样,如果你想让它在任何情况下终止,那么只要

kill $pid

否则将出现竞态条件,即进程可能在第一个kill -0之后消失。

如果你想忽略kill的文本输出,并基于退出码做一些事情,你可以

if ! kill $pid > /dev/null 2>&1; then
echo "Could not send SIGTERM to process $pid" >&2
fi

你有两种方法:

让我们开始寻找一个特定的应用程序在我的笔记本电脑:

[root@pinky:~]# ps fax | grep mozilla
3358 ?        S      0:00  \_ /bin/sh /usr/lib/firefox-3.5/run-mozilla.sh /usr/lib/firefox-3.5/firefox
16198 pts/2    S+     0:00  \_ grep mozilla

现在所有的例子都将寻找PID 3358

第一个方法:为第二列中的PID运行ps auxgrep。在这个例子中,我查找firefox,然后查找PID:

[root@pinky:~]# ps aux | awk '{print $2 }' | grep 3358
3358

所以你的代码将是:

if [ ps aux | awk '{print $2 }' | grep -q $PID 2> /dev/null ]; then
kill $PID
fi

第二种方式:在/proc/$PID目录中查找。我在这个例子中使用exe,但你可以使用其他任何东西。

[root@pinky:~]# ls -l /proc/3358/exe
lrwxrwxrwx. 1 elcuco elcuco 0 2010-06-15 12:33 /proc/3358/exe -> /bin/bash

所以你的代码将是:

if [ -f /proc/$PID/exe ]; then
kill $PID
fi

顺便问一句:kill -9 $PID || true有什么问题?


编辑:

考虑了几个月后…(大约24…)我在这里给出的最初想法是一个很好的hack,但非常不可移植。虽然它教授了一些Linux的实现细节,但它将无法在Mac、Solaris或*BSD上工作。它甚至可能在未来的Linux内核上失败。请-使用"ps"如其他 反应中所述。

最好的方法是:

if ps -p $PID > /dev/null
then
echo "$PID is running"
# Do something knowing the pid exists, i.e. the process with $PID is running
fi

kill -0 $PID的问题是,即使进程正在运行,并且你没有权限终止它,退出码也将是非零。例如:

kill -0 $known_running_pid

而且

kill -0 $non_running_pid

有一个非零的退出码,这些退出码对于普通用户来说是难以区分的,但其中一个假设正在运行,而另一个则没有。


部分相关的,由AnrDaemon提供的附加信息:init进程(PID 1)肯定在所有Linux机器上运行,但不是所有POSIX系统都是Linux。PID 1不保证存在:

kill -0 1
-bash: kill: (1) - No such process …

讨论

如果测试的主体是一个"kill",那么讨论kill和race条件的答案是完全正确的。我是来找将军的。

/proc方法很有趣,但在某种意义上破坏了ps命令抽象的精神,即你不需要在/proc中查找,因为如果Linus决定调用exe文件怎么办?

看起来你想要

wait $PID

它将在$pid结束时返回。

否则你可以使用

ps -p $PID

检查进程是否仍然活跃(这比kill -0 $pid更有效,因为即使你不拥有pid,它也会工作)。

在实现了procfs接口的系统上,比如Linux,你可以检查/proc/$PID是否存在:

if test -d /proc/"$PID"/; then
echo "process exists"
fi

否则你可以使用ps程序:

if [ -n "$(ps -p $PID -o pid=)" ]

在后一种形式中,-o pid=是一种输出格式,只显示进程ID列,没有标题。对于非空字符串操作符-n,引号是必要的,以给出有效的结果。

例如,在GNU/Linux中,你可以使用:

Pid=$(pidof `process_name`)


if [ $Pid > 0 ]; then


do something
else


do something
fi

或者类似的东西

Pin=$(ps -A | grep name | awk 'print $4}')
echo $PIN

它会显示应用程序的名称,只有名称,没有ID。

在这里,我将PID存储在一个名为. PID的文件中(这有点像/run/…),只在尚未执行的情况下执行脚本。

#!/bin/bash
if [ -f .pid ]; then
read pid < .pid
echo $pid
ps -p $pid > /dev/null
r=$?
if [ $r -eq 0 ]; then
echo "$pid is currently running, not executing $0 twice, exiting now..."
exit 1
fi
fi


echo $$ > .pid


# do things here


rm .pid

注意:存在一个竞态条件,因为它不检查pid是如何被调用的。如果系统重新启动,.pid存在,但被不同的应用程序使用,这可能会导致“不可预见的后果”。

pid:

pgrep [pid] >/dev/null

的名字:

pgrep -u [user] -x [name] >/dev/null

- x"意思是“完全匹配”。

从@FDS的回答中学习并点赞,因为它是好的和正确的。但是,这里有一个我觉得更容易阅读和理解的表格。

所以,这是我最喜欢的版本:

解释:

  1. "$?"部分表示“前一个命令的退出或返回代码”。
  2. 如果指定的PID ("$pid")正在运行,ps --pid "$pid"命令将返回退出码0,如果没有,则返回其他数字。
  3. 执行> /dev/null将丢弃打印到stdout的所有输出,因为我们不想看到它。相反,我们想要的只是ps --pid "$pid"命令的退出码("$?"),以查看PID是否正在运行。更具体地说,使用> /dev/nullstdout输出重定向到/dev/null伪文件,其目的是丢弃所有传入的输入。
pid=1234
ps --pid "$pid" > /dev/null
if [ "$?" -eq 0 ]; then
echo "PID $pid exists and is running."
fi

运行shellcheck path/to/this_script.sh告诉我,我实际上应该以另一种方式(如@FDS所示)来避免冗余。查看这里的shellcheck输出:

eRCaGuy_hello_world/bash$ shellcheck check_if_pid_exists.sh


In check_if_pid_exists.sh line 46:
if [ "$?" -eq 0 ]; then
^--^ SC2181: Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.


For more information:
https://www.shellcheck.net/wiki/SC2181 -- Check exit code directly with e.g...

尤其要注意这部分:

SC2181:直接用例如。'if mycmd;',而不是间接使用$?

查看此错误代码的完整描述:https://github.com/koalaman/shellcheck/wiki/SC2181

因此,shellcheck推荐这样的形式:

pid=1234
if ps --pid "$pid" > /dev/null; then
echo "PID $pid exists and is running."
fi

如果你想适应那样做,那就去做吧。但是,如果你认为我的方法更容易阅读和理解,所以你更愿意用我的方法,那么你可以在该行上方添加# shellcheck disable=SC2181来禁用shellcheck警告,就像这样:

这是我最终的和首选的答案

ps --pid "$pid" >/dev/null
# shellcheck disable=SC2181
if [ "$?" -eq 0 ]; then
echo "PID $pid exists and is running."
else
echo "PID $pid does NOT exist."
fi

然而,这里的关键是永远不要在被检查的命令(在本例中是ps)和用__ABC3检查错误代码之间插入任何命令,甚至不要插入__ABC0或__ABC1语句,否则检查错误代码将意外地从最新的命令检查错误代码,例如插入__ABC0或__ABC1语句,而不是从感兴趣的命令(在本例中是ps)!

这就是shellcheck推荐他们所做的主要原因:他们想要确保你在检查正确命令的错误代码。

除此之外,它只是一个迂腐的基于意见的讨论,相当于C中关于如何应该检查NULL(空指针)值的争论:

// The way preferred by pedantic people who don't want to "be redundant" with
// an "unnecessary" `== NULL` check:
if (!some_ptr)
{
printf("ERROR: null ptr.\n");
return;
}


// Versus the more-readable and understandable way which I prefer to use
// whenever my peers will approve it without a fight or long argument
if (some_ptr == NULL)       // in C
// if (some_ptr == nullptr) // in C++
{
printf("ERROR: null ptr.\n");
return;
}


// Note: I just want to get my code merged and move on with life, so if they
// won't easily approve my preferred version above, I'll switch to the other,
// more-pedantic version of code to try to get a quick approval so I can be
// more productive.
// There are few things more discouraging than being blocked over
// silly "pedantry" when your code is correct, bug-free, well-written, and does
// what it says it does.

因此,正如在C中选择哪种方式检查NULL值(空ptrs)纯粹是一个品味问题,在bash中选择哪种方式检查错误代码/返回代码也纯粹是一个品味问题。目前,我更喜欢上面的版本,我已经标记为“我最终和首选的答案”。

不管怎样,这是一个完整、可运行的程序

< >强check_if_pid_exists.sh < / >强从我的eRCaGuy_hello_world回购:

#!/usr/bin/env bash


pid=1234


if [ "$#" -gt 0 ]; then
# At least 1 argument was passed in, so assume it is the PID
pid="$1"
fi


# Try to print the process (`ps`) information for this PID. Send it to
# /dev/null, however, so we don't actually have to look at it. We just want
# the return code, `$?`, which will be 0 if the process exists and some other
# number if not.
ps --pid "$pid" > /dev/null
# shellcheck disable=SC2181
if [ "$?" -eq 0 ]; then
echo "PID $pid exists and is running."
else
echo "PID $pid does NOT exist."
fi

示例运行调用和输出:

eRCaGuy_hello_world/bash$ ./check_if_pid_exists.sh 28876
PID 28876 exists and is running.


eRCaGuy_hello_world/bash$ ./check_if_pid_exists.sh
PID 1234 does NOT exist.


eRCaGuy_hello_world/bash$ ./check_if_pid_exists.sh 5678
PID 5678 does NOT exist.

你可以找到有效的PID(进程id)发送到我的脚本通过首先运行ps aux,并选择一个PID传递给它。

相关的

  1. 我对如何在bash中等待几个子进程完成,并在任何子进程结束code !=0时返回退出代码!=0?的回答