这里的bash脚本文档没有按预期运行

时间:2013-12-13 20:40:13

标签: bash

这是一个失败的最小测试用例

#!/bin/tcsh

#here is some code in tcsh I did not write which spawns many processes.
#let us pretend that it spawns 100 instances of stupid_test which the user kills
#manually after an indeterminate period

/bin/bash <<EOF
#!/bin/bash
while true
do
if [[ `ps -e | grep stupid_test | wc -l` -gt 0 ]]
then
  echo 'test program is still running'
  echo `ps -e | grep stupid_test | wc -l`
  sleep 10
else
  break
fi
done
EOF

echo 'test program finished'

stupid_test程序由

组成
#!/bin/bash
while true; do sleep 10; done

预期的行为是在stupid_test被杀死之前运行(在这种情况下由用户手动),然后在接下来的十秒内终止。观察到的行为是脚本没有终止,并且即使在程序被杀死之后评估ps -e | grep stupid_test | wc -l == 1(并且它不再显示在ps下)

如果直接运行bash脚本,而不是在此文档中运行,则会恢复预期的行为。

我觉得我做的事情非常愚蠢,我不是最有经验的贝壳黑客。为什么要这样做?

1 个答案:

答案 0 :(得分:3)

通常当您尝试grep进程名称时,您会获得grep本身的额外匹配行,例如:

$ ps xa | grep something
57386 s002  S+     0:00.01 grep something

因此,即使没有匹配的过程,您也会得到一条匹配的行。您可以通过在管道中添加grep -v grep来解决此问题:

ps -e | grep stupid_test | grep -v grep | wc -l

正如tripleee建议的那样,更好的解决办法就是像这样写grep

ps -e | grep [s]tupid_test

模式的含义完全相同,但这样它就不再匹配grep本身了,因为字符串"grep [s]tupid_test"与正则表达式/[s]tupid_test/不匹配。

顺便说一句,我会像这样重写你的剧本,清洁:

/bin/bash <<EOF
while :; do
  s=$(ps -e | grep [s]tupid_test)
  test "$s" || break
  echo test program is still running
  echo "$s"
  sleep 10
done
EOF

或者更懒惰但也许是足够的变体(由bryn暗示):

/bin/bash <<EOF
while ps -e | grep [s]tupid_test
do
  echo test program is still running
  sleep 10
done
EOF