脚本中ps aux和`ps aux`之间的结果不同

时间:2016-01-02 14:46:33

标签: linux bash shell

我有一个bash脚本( ScreamDaemon.sh ),其中添加了一个已经运行的示例的检查。

numscr=`ps aux | grep ScreamDaemon.sh | wc -l`;
if [ "${numscr}" -gt "2" ]; then
  echo "an instance of ScreamDaemon still running";
  exit 0;
fi

通常,如果没有另一个脚本副本在运行, ps aux | grep ScreamDaemon.sh | wc -l <​​/ strong>应该返回 2 (它应该找到自己和 grep ScreamDaemon.sh ),但它会返回 3

所以,我试着分析一下发生了什么,并在添加一些回声后看到:

我在剧本中添加了一些行

ps aux | grep ScreamDaemon.sh
ps aux | grep ScreamDaemon.sh | wc -l
str=`ps aux | grep ScreamDaemon.sh`
echo $str
numscr=`ps aux | grep ScreamDaemon.sh | wc -l`;
echo $numscr

有一个输出:

pamela   27894  0.0  0.0 106100  1216 pts/1    S+   13:41   0:00 /bin/bash ./ScreamDaemon.sh
pamela   27899  0.0  0.0 103252   844 pts/1    S+   13:41   0:00 grep ScreamDaemon.sh
2
pamela 27894 0.0 0.0 106100 1216 pts/1 S+ 13:41 0:00 /bin/bash ./ScreamDaemon.sh pamela 27903 0.0 0.0 106100 524 pts/1 S+ 13:41 0:00 /bin/bash ./ScreamDaemon.sh pamela 27905 0.0 0.0 103252 848 pts/1 S+ 13:41 0:00 grep ScreamDaemon.sh
3

我还尝试在`ps aux |中添加 sleep 命令grep ScreamDaemon.sh;睡1m` 并从并行终端看到有多少实例 ps aux | grep ScreamDaemon.sh 显示:

[pamela@pm03 ~]$ ps aux | grep ScreamDaemon.sh
pamela   28394  0.0  0.0 106100  1216 pts/1    S+   14:23   0:00 /bin/bash ./ScreamDaemon.sh
pamela   28403  0.0  0.0 106100   592 pts/1    S+   14:23   0:00 /bin/bash ./ScreamDaemon.sh
pamela   28408  0.0  0.0 103252   848 pts/9    S+   14:23   0:00 grep ScreamDaemon.sh

所以,似乎      str =`ps aux | grep ScreamDaemon.sh` 与之相反      ps aux | grep ScreamDaemon.sh 发现了两个 ScreamDaemon.sh 的实例,但为什么呢?这个额外的 ScreamDaemon.sh 副本来自何处?

这是pstree -ap命令的输出

  │   ├─sshd,27806
  │   │   └─sshd,27808
  │   │       └─bash,27809
  │   │           └─ScreamDaemon.sh,28731 ./ScreamDaemon.sh
  │   │               └─ScreamDaemon.sh,28740 ./ScreamDaemon.sh
  │   │                   └─sleep,28743 2m

2 个答案:

答案 0 :(得分:2)

为什么单个bash脚本可以在ps中多次显示?

当隐式创建子shell的任何构造都在进行中时,这是典型的。例如,在bash中:

echo foo | bar

...创建一个新的shell分叉副本以运行echo,并使用自己的ps实例。类似地:

( bar; echo done )

...创建一个新的子shell,让子shell运行外部命令bar,然后使子shell执行echo

类似地:

foo=$(bar)

...为命令替换创建一个子shell,在那里运行bar(可能exec'命令并使用子shell,但这不保证),并将其输出读入父母。

现在,这是如何回答你的问题的?因为

result=$(ps aux | grep | wc)

...在子shell中运行ps命令 ,它本身会创建一个额外的bash实例。

如何正确确保我的脚本只运行一个副本?

使用锁文件。

参见例如:

请注意,我强烈建议使用基于flock的变体。

答案 1 :(得分:0)

当然,您找到其他流程的原因是:

一个进程正在运行子shell(命令执行`..`
包含在您的行中:numscr=`ps aux | grep ScreamDaemon.sh | wc -l`;

这是最简单的答案。

但是,我想就您的代码提出一些其他建议:

首先,引用你的扩展,它应该是:echo "$str" 不这样做会让几条线路变成长线。

其次,您可以使用:grep [S]creamDaemon.sh来避免匹配grep命令本身。

第三,在变量中捕获命令一次,然后从变量中计算行数。在这种情况下,它没有问题,但对于动态过程,一次捕获和以下捕获计数可能会产生不同的结果。

第四,养成使用$(...) command substitutions而不是更容易出错的习惯(特别是在嵌套时)`...`

### Using a file as the simplest way to capture the output of a command
### that is running in this shell (not a subshell).
ps aux | grep "[S]creamDaemon.sh" > "/tmp/tmpfile$$.txt"  

str="$(< "/tmp/tmpfile$$.txt")"                ### get the value of var "str"
rm "/tmp/tmpfile$$.txt"                        ### erase the file used ($$ is pid).

numscr="$(echo "$str" | wc -l)"                ### count the number of lines.
echo "$numscr"                                 ### present results.
echo "$str"

str="$( ps aux | grep "[S]creamDaemon.sh" )"   ### capture var "str".
numscr="$(echo "$str" | wc -l)"                ### count the number of lines.
echo "$numscr"                                 ### present results.
echo "$str"

### The only bashim is the `$(<...)`, change to `$(cat ...)` if needed.

@CharlesDuffy很好地涵盖了flockplease read it