Bash脚本错误地计算自己的实例

时间:2016-11-17 10:42:42

标签: linux bash grep ps

我创建了一个bash脚本,用于计算已启动的实例。

这是(在这个例子中,我是显示实例而不是用wc -l计算它们):

#!/bin/bash
nb=`ps -aux | grep count_itself.sh`
echo "$nb"
sleep 20

(当然,我的脚本名为count_itself.sh

执行它时,我希望它返回两行,但它返回三行:

root@myserver:/# ./count_itself.sh
root    16225   0.0 0.0 12400   1176 pts/0  S+  11:46   0:00    /bin/bash ./count_itself.sh
root    16226   0.0 0.0 12408   564 pts/0   S+  11:46   0:00    /bin/bash ./count_itself.sh
root    16228   0.0 0.0 11740   932 pts/0   S+  11:46   0:00    grep count_itself.sh

&标志执行它(即在后台,并手动执行ps -aux位,它返回两个,这就是我想要的:

root@myserver:/# ./count_itself.sh &
[1] 16233
root@myserver:/# ps -aux | grep count_itself.sh
root     16233  0.0  0.0  12408  1380 pts/0    S    11:48   0:00 /bin/bash ./count_itself.sh
root     16240  0.0  0.0  11740   944 pts/0    S+   11:48   0:00 grep --color=auto count_itself.sh

我的问题是:为什么脚本中的ps -aux执行会比预期更多地返回一行?

或者,换句话说,为什么在我的第一个示例中创建了标识16226的流程?

编辑(因为大多数人似乎误解了我的问题):

我想知道为什么bash执行返回/bin/bash ./count_itself.sh的两个实例,而不是它返回grep count_itself.sh的原因。

编辑2:

当然,我正在寻找一种避免此行为的方法,并让脚本只返回/bin/bash ./count_itself.sh一次。

4 个答案:

答案 0 :(得分:4)

这是list grep输出的标准问题。

一种解决方案是在角色周围添加一些方括号

ps

这意味着您的nb=$(ps -aux | grep '[c]ount_itself.sh') 实例与自身不匹配,因为带有参数的进程名称包含方括号,但它匹配的模式不包含。

如评论中所述,您应该在变量周围使用双引号以保留空格。

您的结果中似乎有两个相同shell实例的原因是命令替换是在子shell中执行的。有关仅显示父进程的详细信息,请参阅this question

答案 1 :(得分:2)

进程替换需要父shell启动子shell,即在子shell中分叉并执行指定的命令。这是必要的,这样父shell就不会受到$(...)中包含的脚本对环境的任何更改(变量,当前工作目录,陷阱)的影响。

示例:

$ cat test.sh 
#!/bin/bash

a=1
b="$(a=2; echo abc)"
echo "a=$a"
echo "b=$b"
$ ./test.sh 
a=1           # Note that the variable 'a' preserved its value
b=abc

由于分叉,您正在看到一个额外的脚本实例。

我不认为可以从输出中可靠地消除那些不需要的进程,因为原则上,脚本可以合法地启动自身的另一个实例(它将作为子进程运行),而你不能区分这两种情况。

一个hacky解决方案是让脚本在调用时在指定位置创建(例如在/tmp/your_script_name)PID文件,并在终止时将其删除。

答案 2 :(得分:2)

我建议下一步:

排除父亲是我自己的所有流程:

 ps --pid $$ -N -a | grep count_itself.sh

这意味着显示父亲不是我自己的所有命令(所以这会排除你的grep进程和你的fork进程来执行反句)

答案 3 :(得分:1)

终于找到了一种方法,尽管是一种丑陋的方式,部分地受到@TomFenech在answer中链接的问题的启发:

#!/bin/bash
nb=$(ps f | grep '[c]ount_itself.sh' | grep -v '    \\_')
echo "$nb"
sleep 20

执行:

root@myserver:/# ./count_itself.sh
17725 pts/1    S+     0:00  \_ /bin/bash ./count_itself.sh

执行已经在bg中运行的一个:

root@myserver:/# ./count_itself.sh &
[1] 17733
root@myserver:/# ./count_itself.sh
17733 pts/1    S      0:00  \_ /bin/bash ./count_itself.sh
17739 pts/1    S+     0:00  \_ /bin/bash ./count_itself.sh

解释(来自我所理解的):

  • ps f返回活动进程树
  • grep '[c]ount_itself.sh'将上一个命令限制为仅显示count_itself.sh
  • 的实例

返回

17808 pts/1    S+     0:00  \_ /bin/bash ./count_itself.sh
17809 pts/1    S+     0:00      \_ /bin/bash ./count_itself.sh
  • grep -v ' \\_'排除包含4个空格(相当于标签)的行,然后排除\_对应于子流程的行