2

我有一个在 unix 中运行的程序(我无法控制),完成后打印“成功完成”但不退出。我想自动检测进程何时完成(通过检查命令的输出),以便我可以终止进程并继续执行其他活动。之所以复杂,是因为我希望能够同时运行多个这些脚本。(我需要做的一项活动需要使用各种输入调用脚本,但是每次脚本运行都需要一段时间才能返回,所以我想并行执行)

有没有人做过类似的事情?我可以将命令的 stderr 和 stdout 输出重定向到一个具有随机文件名的临时文件,然后将该文件拖尾并通过管道传输到 grep 以获取结束条件(即某些日志行)。问题是,tail -f 肯定会继续运行,因此它永远不会退出。我应该投票吗?如果是这样,最好的方法是什么?

4

7 回答 7

2

你不必投票。您可以使用inotify工具(例如inotifywait)来检测输出文件何时发生更改,然后 grep 并在必要时终止该进程。狩猎愉快!

于 2010-04-22T07:51:38.180 回答
2

#!/usr/bin/expect

spawn /home/foo/myjob
期望“成功完成”

于 2010-04-22T20:08:08.773 回答
1

这是我的尝试,它工作得很好,但是相当垃圾,因为每次 kill 被称为一个被杀死的消息,回显整个 subshel​​l 脚本被输出到控制台,我似乎无法抑制它。如果我找到摆脱垃圾邮件的方法,我会更新,(顺便说一下,将 stdout 和 stderr 重定向到 /dev/null 没有帮助。)

注意:你需要 bash4 来运行它;$BASHPID仅从 4 开始可用,并且$$不是替代方案,因为它给出了父 shell 的 pid,而不是子 shell。

!/bin/bash

for i in {1..8}; do
  (
    mypid=$BASHPID
    (
      #subshell to simulate started process
      sleep 2
      echo finished
      while : ; do
        :
      done
    ) | \
    {
      while read line; do
        if [[ $line == finished ]]; then
          break;
        fi
      done
      echo process done, killing $mypid...
      kill -9 $mypid
    }
  ) &
done
wait
于 2010-04-22T09:40:56.630 回答
1

以下包装脚本将调用您的真实命令,将输出通过管道传输到tee,该 tee 将写入 fifo。当预期的字符串从 fifo中被grep时,包装器脚本将终止您的真实命令:

#!/bin/bash

# cmd to run
expected_output=$1
shift
cmd=("$@")
# where to read commands output
mkfifo /tmp/killonoutput.$$
# start cmd async 
"${cmd[@]}"|tee /tmp/killonoutput.$$ 2>/dev/null &

if grep -q --max-count=1 "$expected_output" /tmp/killonoutput.$$;then
    kill %1 
    echo "Killed ${cmd[@]}" 
fi
# grep returned so fifo was closed
rm  /tmp/killonoutput.$$

示例执行:

./killonoutput.sh "Finished" bash -c "echo sleeping;sleep 3;echo Finished; sleep 10000" 
sleeping
Finished
Killed bash -c echo sleeping;sleep 3;echo Finished; sleep 10000
./killonoutput.sh: line 17: 19553 Terminated              "${cmd[@]}"
     19554                       | tee /tmp/killonoutput.$$ 2> /dev/null
于 2010-04-22T16:40:10.590 回答
0

这对于 bash 来说可能有点矫枉过正,也许吧?如果您使用的是 python,您可以使用该subprocess模块在您自己的程序和您的子脚本之间保持通信通道打开。更好的是,您可以使用该threading模块并行启动子脚本的多次运行。

脚本输出“成功完成”后是否输出任何内容?如果没有,您可以轮询它输出的最后 x 个字符(到您建议的随机文件,可能tail -n 1用于获取最后一行),看看您是否刚刚看到“成功完成”。但是如果脚本之后继续打印东西,你可能会错过这一行。

或者你可以grep在输出中“成功完成”。

于 2010-04-22T07:51:58.080 回答
0

一些随意的想法。(1) 如果它是你的程序,你可以修改它以更有用地指示完成。(2) 查看进程列表 ( ps ax ),您应该能够看到它在尝试输出“已完成”消息时进入 I/O 等待状态。(3) 我相信伪 tty 的存在就是为了这个目的,你可以直接观看他们的标准输出,而不必处理临时文件。

于 2010-04-22T07:53:43.287 回答
0

第一种可能的解决方案:
your_program > tempfile1
grep -i "Completed successfully" tempfile1; 如果 [ $? -eq 0 ] ; 然后 i=`pidof your_program`; 杀死$i; 菲

首先是重定向。其次检查 grep 的退出状态。如果它是 0(成功),它会获取程序的 pid 并杀死它。
这是一个“快速思考”的练习。它应该工作。一个缺点是您一次只能运行一个程序实例,但当然可以进行调整并且您可以并行运行它。

edit-27.05.2010:
第二个可能的解决方案:
tail --pid=`pidof your_program` -f -n0 tempfile1 | awk '/Completed/ {if ($1=="Completed") {system("i=`pidof your_program`; kill $i")}}' &

好吧,有了这条长线,您不再需要循环/crontab。解释:--
pid=`pidof your_program` 在指定的 PID 结束后使 tail 死掉。这样,当程序被杀死时,tail 也会随之死亡。
-n0 忽略 tempfile1 中的其他“已完成”语句(因此您不必担心总是先截断 tempfile1)。
awk 搜索您的程序抛出的“已完成”语句,该{system}部分在您的程序上执行 kill。
也不要忘记&最后的,因为它是整个事情的背景。
玩得开心。

于 2010-04-22T09:22:45.373 回答