为什么在反引号中执行命令会在脚本中完成时给出不同的结果?

时间:2011-08-18 19:57:18

标签: linux bash

我有一个脚本,我的意思是从cron运行,确保我写的守护进程正在运行。脚本文件的内容类似于以下内容:

daemon_pid=`ps -A | grep -c fsdaemon`
echo "daemon_pid: " $daemon_pid
if [ $daemon_pid -eq 0 ]; then
   echo "restarting fsdaemon"
   /etc/init.d/fsdaemon start
fi

当我从命令提示符执行此脚本时,回显$ daemon_pid值的行报告的值为2.无论我的守护程序是否正在运行,该值都是2。但是,如果我使用后引号执行命令然后检查$ daemon_pid变量,则$ daemon_pid的值现在为1。我也尝试使用bashdb单步执行脚本,当我使用该工具检查变量时,它们就是它们应该是的。

因此,我的问题是:为什么shell执行脚本与手动执行脚本命令之间的行为存在差异?我确信我缺少一些非常基本的东西。

5 个答案:

答案 0 :(得分:3)

您很有可能在grep的“答案”中遇到ps

为了帮助您全面了解所发生的情况,请关闭-c选项,以查看仅从ps -A | grep fsdameon返回的数据。

为了解决这个问题,一些系统有一个p(rocess)grep(pgrep)。这将有效,或者

ps -A | grep -v grep | grep -c fsdaemon

你会看到一个常见的习语,但却以另一个过程为代价。

最干净的解决方案是,

ps -A | grep -c '[f]sdaemon'

正则表达式语法应该适用于所有系统上的所有greps。

我希望这会有所帮助。

答案 1 :(得分:3)

问题是grep本身出现了......尝试在grep -c之后用任何东西运行这个命令:

eple:~ erik$ ps -a | grep -c asdfladsf
1
eple:~ erik$ ps -a | grep -c gooblygoolbygookeydookey
1
eple:~ erik$ 

ps -a | grep fsdaemon返回什么?只要看看实际列出的流程......:)

答案 2 :(得分:2)

由于这是Linux,为什么不试试pgrep?这样可以节省管道,并且最终不会报告grep报告守护程序脚本本身的运行情况。

答案 3 :(得分:1)

包含该名称的参数的Aany进程将添加到count - grep和您的脚本。

ps进程并不可靠,你应该使用一个锁文件。

答案 4 :(得分:0)

正如有几个人已经指出的那样,你的进程计数被夸大了,因为ps | grep检测到(1)脚本本身和(2)由反引号创建的子进程,后者继承了主脚本的名称。因此,一个简单的解决方案是将脚本的名称更改为不包含您要查找的名称的内容。但你可以做得更好。

我建议的“最佳实践”解决方案是使用操作系统提供的功能。初始化脚本在启动守护进程的过程中创建PID文件的情况并不少见;换句话说,您不是只运行守护程序本身,而是使用启动守护程序的包装器脚本,然后将进程ID写入某个文件。如果您的系统中存在start-stop-daemon(我认为这些日子相当普遍),您可以这样使用:

start-stop-daemon --start --quiet --background \
     --make-pidfile --pidfile /var/run/fsdaemon.pid -- /usr/bin/fsdaemon

(显然替换路径/usr/bin/fsdaemon)以启动它,然后

start-stop-daemon --stop --quiet --pidfile /var/run/fsdaemon.pid

阻止它。 start-stop-daemon还有其他可能对您有用的选项,您可以通过阅读手册页进行调查。

如果您无权访问start-stop-daemon,您可以编写一个包装脚本来执行基本相同的操作,这样就可以了:

echo "$$" > /var/run/fsdaemon.pid
exec /usr/bin/fsdaemon

然后停止:

kill $(< /var/run/fsdaemon/pid)
rm /var/run/fsdaemon.pid

(当然,这非常粗糙,但通常应该有效)。

无论如何,一旦你有了生成PID文件的设置,无论是否使用start-stop-daemon,你都可以将检查脚本更新为:

daemon_pid=`ps --no-headers --pid $(< /var/run/fsdaemon.pid) | wc -l`
if [ $daemon_pid -eq 0 ]; then
    echo "restarting fsdaemon"
    /etc/init.d/fsdaemon restart
fi

(人们会认为会有一个简洁的命令来检查给定的PID是否正在运行,但我不知道它。)

如果您不想(或不能)创建PID文件,我至少会建议pgrep而不是ps | grep,因为pgrep将直接搜索按名称处理,不会发现恰好包含相同字符串的任何内容。

daemon_pid=`pgrep -x -c fsdaemon`
if [ $daemon_pid -eq 0 ]; then
    echo "restarting fsdaemon"
    /etc/init.d/fsdaemon restart
fi

-x表示“完全匹配”,-c表示与grep一样。

顺便说一下,当变量daemon_pid实际上是一个计数时,将它命名为{{1}}似乎有点误导。