为什么我会得到不同的 ps 退出状态 | grep 在脚本中?

为什么我会得到不同的 ps 退出状态 | grep 在脚本中?

我正在运行以下脚本:

#!/bin/bash

ps ax  | grep -q [v]arnish
if [ $? -eq 0 ];then
        echo varnish is running...
        exit 0
else
        echo "Critical : varnish is not running "
        exit 2
fi

输出就像::

[root@server ~]# sh -x check_varnish_pro.sh
+ ps ax
+ grep -q '[v]arnish'
+ '[' 0 -eq 0 ']'
+ echo varnish is running...
varnish is running...
+ exit 0

当我在命令行中运行相同命令时,我得到的退出状态为 1:

[root@server ~]# ps ax  | grep -q [v]arnish; echo $?
1

这种情况就像服务器中没有安装varnish一样。该脚本在安装了 varnish 的服务器中运行良好。

为什么使用脚本和命令行运行时退出状态不同?如何改进这个脚本?

答案1

一般来说,尝试使用简单的方法来确定给定进程是否正在运行是一个坏ps主意grep

你最好使用pgrep以下方法:

if pgrep "varnish" >/dev/null; then
  echo "Varnish in running"
else
  echo "Varnish is not running"
fi

请参阅手册pgrep。在某些系统上(可能不在 Linux 上),您会得到一个-q与同一标志相对应的标志,从而grep无需重定向到/dev/null.还有一个-f标志可以在完整的命令行上执行匹配,而不仅仅是进程名称。还可以将匹配限制为属于使用 的特定用户的进程-u

安装后pgrep您还可以访问它,pkill从而可以根据进程的名称向进程发出信号。

还,如果这是一个服务守护进程,如果您的 Unix 系统有一种方法来查询它的信息(例如,它是否已启动并正在运行),那么这就是恰当的检查它的方法。

在 Linux 上,您有systemctlsystemctl is-active --quiet varnish如果正在运行,则返回 0,否则返回 3),在 OpenBSD 上,您有rcctl,等等。


现在到你的脚本:

在您的脚本中,您解析 的输出ps ax。此输出将包含脚本本身的名称 ,check_varnish_pro.sh其中显然包含字符串varnish。这会给您带来误报。如果您在测试时在没有-q标志的情况下运行它,您会发现这一点。grep

#!/bin/bash
ps ax | grep '[v]arnish'

运行它:

$ ./check_varnish_pro.sh
31004 p1  SN+     0:00.04 /bin/bash ./check_varnish_pro.sh

另一个问题是,尽管您尝试通过在模式中使用来“隐藏”grep进程,使其不被自身检测到。如果您碰巧在其中包含指定文件或目录的目录中运行脚本或命令行,则该方法将失败(在这种情况下,您将再次得到误报)。这是因为该模式未加引号,并且 shell 将使用它执行文件名通配。grep[v]varnish

看:

bash-4.4$ set -x
bash-4.4$ ps ax | grep [v]arnish
+ ps ax
+ grep '[v]arnish'
bash-4.4$ touch varnish
+ touch varnish
bash-4.4$ ps ax | grep [v]arnish
+ ps ax
+ grep varnish
91829 p2  SN+p    0:00.02 grep varnish

文件的存在varnish将导致 shell 替换[v]arnish为文件名varnish,并且您会在进程表(进程)中找到模式grep

答案2

check_varnish_pro.sh当您运行名为test 的脚本时

ps ax  | grep -q [v]arnish

成功是因为有一个名为check__pro跑步。

答案3

@AlexP 解释说非常简洁地描述了实际发生的情况,但是@Kusalananda 的想法pgrep/pkill用于关键过程是强烈劝阻更好的解决方案包括:

  • 询问服务是否正在运行。systemctl status varnishd应该在现代 *nix 安装上解决这个问题。
  • 如果在某些不幸的情况下您没有可用的服务,您可以简单地更改启动脚本以在进程退出时立即报告问题:

    varnish || true
    some_command_to_send_an_alert_that_the_service_has_died
    
  • 或者将启动服务的脚本更改为记录PID,然后使用 定期检查状态kill -0 "$pid"

相关内容