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
只有一次。
这是 grep
ing 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 个空格(相当于制表符)然后 \_
的行,这对应于子进程
我创建了一个 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
只有一次。
这是 grep
ing 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 个空格(相当于制表符)然后\_
的行,这对应于子进程