Linux/Unix命令以确定进程是否正在运行?

112

我需要一个跨平台的(Linux/Unix|OSX)shell/bash命令,用于确定特定进程是否正在运行。例如:mysqldhttpd...

最简单的方法/命令是什么?

15个回答

185

虽然 pidofpgrep 是很好的确定正在运行的进程的工具,但它们在某些操作系统上不可用。一个明确的备选方案是使用以下命令:ps cax | grep command

Gentoo Linux 上的输出:

14484 ?        S      0:00 apache2
14667 ?        S      0:00 apache2
19620 ?        Sl     0:00 apache2
21132 ?        Ss     0:04 apache2

OS X 上的输出:

42582   ??  Z      0:00.00 (smbclient)
46529   ??  Z      0:00.00 (smbclient)
46539   ??  Z      0:00.00 (smbclient)
46547   ??  Z      0:00.00 (smbclient)
46586   ??  Z      0:00.00 (smbclient)
46594   ??  Z      0:00.00 (smbclient)

在 Linux 和 OS X 上,grep 返回一个退出码,所以很容易检查进程是否被找到:

#!/bin/bash
ps cax | grep httpd > /dev/null
if [ $? -eq 0 ]; then
  echo "Process is running."
else
  echo "Process is not running."
fi

此外,如果你想要进程ID列表,也可以很容易地使用grep实现:

ps cax | grep httpd | grep -o '^[ ]*[0-9]*'

这个命令在Linux和OS X上都可以使用:

3519 3521 3523 3524

下面的命令输出空字符串,因此对于没有运行的进程使用这种方法是安全的:

echo ps cax | grep aasdfasdf | grep -o '^[ ]*[0-9]*'

这种方法适合编写一个简单的空字符串测试,然后遍历发现的进程ID。

#!/bin/bash
PROCESS=$1
PIDS=`ps cax | grep $PROCESS | grep -o '^[ ]*[0-9]*'`
if [ -z "$PIDS" ]; then
  echo "Process not running." 1>&2
  exit 1
else
  for PID in $PIDS; do
    echo $PID
  done
fi

您可以通过将其保存到一个带有执行权限的文件中(命名为“running”)并使用参数执行它来测试它:./running "httpd"

#!/bin/bash
ps cax | grep httpd
if [ $? -eq 0 ]; then
  echo "Process is running."
else
  echo "Process is not running."
fi

警告!!!

请记住,您仅仅是解析ps ax的输出,这意味着,就像在Linux中看到的那样,它不仅仅匹配进程,还包括传递给该程序的参数。当使用此方法时,我强烈建议尽可能具体(例如,./running "mysql"将匹配'mysqld'进程)。如果可能,请使用which检查完整路径。


参考资料:

http://linux.about.com/od/commands/l/blcmdl1_ps.htm

http://linux.about.com/od/commands/l/blcmdl1_grep.htm


2
抱歉,虽然从语义角度来看答案肯定是正确的,但我完全反对通过模式匹配过程参数向量来寻找进程的方法。任何这样的方法迟早都会失败(你实际上承认了这一点,因为你说需要进行更多的检查)。我已经在另一个答案中添加了我的建议。 - peterh
6
grep 命令会包含自身运行的进程(例如,ps cax | grep randomname 命令总是返回 0 ,因为 grep 命令会找到 grep randomname 的进程(希望这一点很清楚...)。一种解决方法是在进程名称的第一个字母周围添加方括号,例如 ps cax | grep [r]andomname 命令。 - Kyle G.
1
ps cax 可能无法完整输出命令名称。例如,它会打印出 "chromium-browse" 而不是 "chromium-browser"。 - jarno
@KyleG。ps cax | grep randomname grep 不总是会返回0,但是 ps ax | grep randomname 会。 - jarno
@jarno,你说得完全正确,我的错。我应该先测试一下我的建议 ;) - Kyle G.
显示剩余2条评论

31

你应该知道 PID!

尝试对进程参数进行某种模式识别(例如 pgrep“mysqld”)来查找进程是注定要失败的策略。如果有两个mysqld在运行,该怎么办?忘记那个方法吧。你可能暂时能做对,并且它可能会持续一年或两年,但随后会发生你没有考虑过的事情。

只有进程ID(pid)是真正唯一的。

启动后台进程时,请始终存储pid。在Bash中,可以使用 $! Bash变量来完成此操作。这样做会让您省去很多麻烦。

如何确定进程是否正在运行(通过pid)

现在问题变成了如何知道pid是否正在运行。

只需执行:

ps -o pid = -p <pid>

这是POSIX标准,因此具有可移植性。如果进程正在运行,则会返回pid本身,如果进程未运行,则不会返回任何内容。严格来说,该命令将返回一个单独的列,即 pid ,但由于我们给出了一个空标题头(紧接着等号的内容),而且这是唯一请求的列,因此 ps 命令将根本不使用标题。这就是我们想要的,因为它使解析变得更容易。

这适用于Linux、BSD、Solaris等操作系统。

另一种策略是测试上述 ps 命令的退出值。如果进程正在运行,则应该为零,如果没有,则为非零。 POSIX规范说,如果发生错误, ps 必须退出> 0,但我不清楚什么构成“错误”。因此,我个人不使用该策略,尽管我相信它在所有Unix / Linux平台上也可以很好地工作。


1
除了这个回答并没有解决问题,即确定服务是否正在运行。在这种情况下,进程ID是未知的,因此只有在您确实知道进程ID的情况下,才能使用这个答案。 - Highway of Life
2
错误。我的评论的整个观点是要退一步并说,如果您首先发现自己处于必须执行某种形式的“grep <sometext>”以查找给定进程的情况中,则在启动进程时您已经做错了什么,依我之见。从OP的问题中我可以看出他确实掌控着如何启动该进程。 - peterh
2
更正确的“术语”应该是“跨平台命令以确定服务是否正在运行”,它不是在运行检查的同一系统,而是在外部系统中,因此 PID 根本不会被知道。 - Highway of Life
2
这并不是绝对可靠的。在系统运行足够长的时间后,您感兴趣的进程可能已经死亡,并且另一个进程可能已被分配与您正在检查的相同PID。 - claymation
1
@claymation。说得好。然而,PID方法仍然比基于进程参数的模式匹配更好,因为PID冲突比意外启动相同服务的两个实例更不可能发生。这只是我的个人看法。 :-) - peterh
显示剩余3条评论

16

在大多数Linux发行版中,你可以使用pidof(8)命令。

它会列出所有正在运行的指定进程的进程ID,如果没有正在运行的实例,则不会列出任何内容。

例如,在我的系统上(我有四个bash实例和一个remmina实例正在运行):

$ pidof bash remmina
6148 6147 6144 5603 21598

在其他Unix系统上,正如其他人所指出的那样,pgrep或者psgrep的组合也可以实现相同的功能。


在Red Hat 5上,“+1 pidof httpd”运行良好。但是在我的Red Hat 4上,没有“pidof” :-( - oHo
实际上,这个命令比我想象的要少见,我编辑了我的答案以使其更加清晰明了。 - Frédéric Hamidi
非常干净的答案,确实如此(在支持的系统上)。谢谢。 - Mtl Dev

8
这应该适用于大多数Unix、BSD和Linux系统:
PATH=/usr/ucb:${PATH} ps aux | grep httpd | grep -v grep

测试环境:

  • SunOS 5.10 [因此需要设置PATH=...]
  • Linux 2.6.32 (CentOS)
  • Linux 3.0.0 (Ubuntu)
  • Darwin 11.2.0
  • FreeBSD 9.0-STABLE
  • Red Hat Enterprise Linux ES release 4
  • Red Hat Enterprise Linux Server release 5

3
+1 简单地使用 ps 命令即可。为了避免第二个 grep,我建议使用:ps aux | grep [h]ttpd - oHo
我这里没有使用方括号技巧,以便更容易将变量放入主要的 grep 中。 - johnsyweb
1
好的 ;) 我刚在 Red Hat AS 4 和 Red Hat AP 5 上进行了测试。当然可以工作!因此,您可以将以下内容添加到您的列表中:Red Hat Enterprise Linux ES release 4Red Hat Enterprise Linux Server release 5。干杯! - oHo
@Downvoter:为什么?我错过了什么吗?据我所知,被接受的答案正在执行相同的查找! - johnsyweb

6

最简单的方法是使用 ps 和 grep:

command="httpd"
running=`ps ax | grep -v grep | grep $command | wc -l`
if [ running -gt 0 ]; then
    echo "Command is running"
else
    echo "Command is not running"
fi

如果你的命令有一些参数,那么在“grep $command”后面可以再加上更多的“grep cmd_arg1”,以过滤掉其他可能不感兴趣的进程。
例如:显示是否正在运行任何带有以下参数的java进程:
-Djava.util.logging.config.file=logging.properties
ps ax | grep -v grep | grep java | grep java.util.logging.config.file=logging.properties | wc -l

2
实际上,使用 ps cax 可以避免使用 grep -v。因此,例如,您可以使用:ps cax | grep java > /dev/null || echo "Java not running" - Highway of Life
1
第三行有一个错误,请将“running”更改为“$running”。 - Programmer
如果不将 running 更改为 $running,您将会收到以下错误信息 [: running: integer expression expected] - nextloop

6

将各种建议结合起来,我能想到的最干净的版本(不使用不可靠的grep触发单词部分)是:

kill -0 $(pidof mysql) 2> /dev/null || echo "Mysql ain't runnin' message/actions"

kill -0命令不会杀死进程,而是检查进程是否存在并返回true。如果您的系统上没有pidof命令,请在启动进程时存储pid:

$ mysql &
$ echo $! > pid_stored

然后在脚本中:

kill -0 $(cat pid_stored) 2> /dev/null || echo "Mysql ain't runnin' message/actions"

5

只需要做一个小改动:如果你在 ps 命令中添加 -c 标志,就不需要再使用 grep -v 命令删除包含 grep 进程的行了。例如:

ps acux | grep cron

如果您在使用类似BSD系统(包括MacOSX)的操作系统,那么 -u 参数就足够了,如果您不需要太多信息可以省略该参数。

如果您使用的是原生 ps 命令遵循 SysV 遗传特征的系统,则应使用以下命令:

ps -e |grep cron

或者

ps -el |grep cron 

如果需要列出包含进程id和进程名称以外的更多信息,可以使用-o <field,field,...>选项选择要打印出的具体字段。


这个答案如何具有可移植性?(您说在不同的平台上应该使用不同形式的ps命令) - peterh
遗憾的是,ps 是那些具有不同选项集的工具之一,这取决于它们的祖先。因此,除非您编写自己的(再次与其他任何内容不兼容的)包装器,否则应该了解主要的继承线并相应地进行调整。当您在编写脚本时情况就不同了 - 在那里,您将使用这些差异来确定您所在的分支,并调整脚本的行为。底线:您需要了解两者。著名例子:Larry Wall 的“configure”脚本。著名引用:“恭喜,您没有运行 Eunice。” - Tatjana Heuser

3

我使用 pgrep -l httpd,但不确定它是否存在于任何平台上...
有谁能在OSX上确认一下吗?


谢谢@Johnsyweb。你能也检查一下pidof吗?好的,你已经检查了。谢谢。所以我们应该找到其他在OSX上工作的东西...你的基本ps|grep可能是唯一的解决方案;-) - oHo

1

没有一个答案对我有用,这是我的解决方案:

process="$(pidof YOURPROCESSHERE|tr -d '\n')"
if [[ -z "${process// }" ]]; then
  echo "Process is not running."
else
  echo "Process is running."
fi

解释:

|tr -d '\n'

这将删除终端创建的回车符。其余内容可以通过this文章解释。


1
这种方法可用于在命令“ps”、“pidof”和其他命令不可用的情况下使用。我个人在我的工具/脚本/程序中经常使用procfs。
   egrep -m1  "mysqld$|httpd$" /proc/[0-9]*/status | cut -d'/' -f3

简要解释:

  1. -m1 - 在第一次匹配时停止进程
  2. "mysqld$|httpd$" - grep将匹配以mysqld或httpd结尾的行
  3. /proc/[0-9]* - bash将匹配以任何数字开头的行
  4. cut - 仅通过分隔符'/'拆分输出并提取第3个字段

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