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

Bash script counting instances of itself wrongly

我创建了一个 bash 脚本,它计算自己的启动实例。

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

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

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

执行后,我希望它有 return 两行,但 return 有三行:

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 位,它 return 是两个,这就是我想要的:

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脚本内部的执行return比预期多了一行?

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

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

我想知道为什么 bash 执行 return 两个 /bin/bash ./count_itself.sh 实例,而不是 return 执行 grep count_itself.sh.

编辑 2:

当然,我正在寻找一种方法来避免这种行为,并且脚本 return /bin/bash ./count_itself.sh 只有一次。

这是 greping ps 输出的标准问题。

一个解决方案是在字符周围添加一些方括号

nb=$(ps -aux | grep '[c]ount_itself.sh')

这意味着您的 grep 实例不匹配自身,因为进程的名称及其参数包含方括号,但它匹配的模式不包含。

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

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

进程替换需要父 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 文件,并在终止时将其删除。

我建议下一种方式:

排除父进程为本人的所有进程:

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

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

终于找到了一种方法,尽管是一种丑陋的方法,部分灵感来自@TomFenech 在他的 中链接的问题:

#!/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

已在背景中 运行 执行:

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 returns 活动进程树
  • grep '[c]ount_itself.sh' 将上一个命令限制为仅显示 count_itself.sh
  • 的实例

Returns

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 个空格(相当于制表符)然后 \_ 的行,这对应于子进程