根据其标准输出终止进程?

根据其标准输出终止进程?

假设我正在运行一个进程,它执行了一个非常冗长的过程,将其进度打印到标准输出。有没有办法在执行完以下操作后自动终止该进程:

  • x 行输出
  • 输出中是否发现了某个关键字?

我目前已经将命令的输出传输x到管道,egrep 'search pattern并希望在显示特定行x后终止。egrep

我想如果有某种方式来编写脚本:

run command `y` after `x` lines of output are seen on previous apps piped `sdout`

我可以相当轻松地实现这一点,例如:

mylongrunningtool | egrep '(term1|term2)' | runafterxlines --lines=8 --command='killall -9 mylongrunnigtool`

有谁愿意接受吗?

答案1

尝试以下head命令:

HEAD(1)                          User Commands                         HEAD(1)

NAME
       head - output the first part of files

SYNOPSIS
       head [OPTION]... [FILE]...

DESCRIPTION
       Print  the  first  10 lines of each FILE to standard output.  With more
       than one FILE, precede each with a header giving the file  name.   With
       no FILE, or when FILE is -, read standard input.

head允许您指定行数。请参阅手册页以获取更多信息。

loop.py

#!/usr/bin/python`

i = 0
while True:
    print "This is line " + str(i)
    i += 1

loop.py应该无限运行,但如果我将其输出传输到head,我会得到:

$ ./loop.py | head 
This is line 0
This is line 1
This is line 2
This is line 3
This is line 4
This is line 5
This is line 6
This is line 7
This is line 8
This is line 9
Traceback (most recent call last):
  File "./loop.py", line 6, in <module>
    print "This is line " + str(i)
IOError: [Errno 32] Broken pipe

请注意,错误(Traceback ...)部分实际上是stderr,如运行所示./loop.py 2> stderr.log | head,因此您不必担心 grepping head 的输出。

最后,搜索:

$ ./loop.py 2> /dev/null | head | grep -n "line 6" 
7:This is line 6

在这里,我已将 重定向stderrloop.py之外,尽管我们确定它不会干扰 和 处理的head文本grep

编辑

总结head:CPU 调度程序控制密集进程完成其输出后将运行多少。

经过一些测试,我发现我的解决方案虽然确实缩短了 的执行时间loop.py,但并不像人们想象的那么强大。对我的 进行以下修改后loop.py,将其输出通过管道传输到 head 会产生以下结果:

新的loop.py

#!/usr/bin/env python

import sys

def doSomethingIntensive():
    # actually do something intensive here
    # that doesn't print to stdout
    pass

i = 0
while True:
    # printing to stderr so output is not piped 
    print >> sys.stderr, (
            "Starting some calculation that " 
            "doesn't print to stdout")
    doSomethingIntensive()
    print >> sys.stderr, "About to print line " + str(i)
    print "This is line " + str(i)
    print >> sys.stderr, "Finished printing line " + str(i)
    i += 1

输出:

$ ./loop.py | head
Starting some calculation that doesn't print to stdout
About to print line 0
Finished printing line 0
Starting some calculation that doesn't print to stdout
About to print line 1
Finished printing line 1
Starting some calculation that doesn't print to stdout
About to print line 2
Finished printing line 2
...
About to print line 247
Finished printing line 247This is line 0
This is line 1
This is line 2
This is line 3
This is line 4
This is line 5
This is line 6
This is line 7
This is line 8
This is line 9

Starting some calculation that doesn't print to stdout
About to print line 248
Finished printing line 248
... 
About to print line 487
Finished printing line 487
Starting some calculation that doesn't print to stdout
About to print line 488
Traceback (most recent call last):
  File "./loop.py", line 18, in <module>
    print "This is line " + str(i)
IOError: [Errno 32] Broken pipe

我隐藏了部分输出,只留下了相关部分。实际上,输出显示head(我猜是所有进程的)标准输入/输出流都是缓冲的。

根据这个答案一旦接收方(head)终止,管道就会断开,并且*只有当发送方loop.py尝试写入现已损坏的管道*将向其发送 SIGPIPE 信号。

因此,当head有机会打印其输出时,所有内容都会立即显示出来,但之后才会loop.py继续显示另外 247 行。(这与进程的调度有关。)此外,在head打印其输出之后但在终止之前,调度程序恢复了运行loop.py,因此在管道中断之前,又有大约 250 行(最多 488 行)被写入管道。

为了获得更好的结果,我们可以使用无缓冲 I/O(在本例中为的无缓冲输出loop.py)。通过使用选项调用 Python 解释器-u,我们得到:

$ python -u loop.py | head
Starting some calculation that doesn't print to stdout
About to print line 0
Finished printing line 0This is line 0

Starting some calculation that doesn't print to stdout
About to print line 1
Finished printing line 1This is line 1

Starting some calculation that doesn't print to stdout
About to print line 2
Finished printing line 2This is line 2

Starting some calculation that doesn't print to stdout
About to print line 3
Finished printing line 3This is line 3

Starting some calculation that doesn't print to stdout
About to print line 4
Finished printing line 4This is line 4

Starting some calculation that doesn't print to stdout
About to print line 5
Finished printing line 5This is line 5

Starting some calculation that doesn't print to stdout
About to print line 6
Finished printing line 6This is line 6

Starting some calculation that doesn't print to stdout
About to print line 7
Finished printing line 7This is line 7

Starting some calculation that doesn't print to stdout
About to print line 8
Finished printing line 8This is line 8

Starting some calculation that doesn't print to stdout
About to print line 9
Finished printing line 9
This is line 9
Starting some calculation that doesn't print to stdout
About to print line 10
Traceback (most recent call last):
  File "loop.py", line 18, in <module>
    print "This is line " + str(i)
IOError: [Errno 32] Broken pipe

当然,如果您的程序是用 Python 编写的,那么这很简单,因为您不需要修改代码。但是,如果它是用 C 编写的,并且您恰好有它的源代码,则可以使用函数setvbuf()设置stdio.hstdout无缓冲:

loop.c

#include <stdio.h>
#include <stdlib.h>
#define TRUE 1

unsigned long factorial(int n)
{
    return (n == 0) ? 1 : n * factorial(n - 1);
}

void doSomethingIntensive(int n)
{
    fprintf(stderr, "%4d: %18ld\n", n, factorial(n));
}

int main()
{
    int i;

    if (!setvbuf(stdout, NULL, _IONBF, 0)) /* the important line */
        fprintf(stderr, "Error setting buffer size.\n");
    for(i=0; TRUE; i++)
    {
        doSomethingIntensive(i);
        printf("This is line %d\n", i);
    }

    return 0;
}

答案2

我认为grep接受答案中的示例没有像 OP 预期的那样工作(即在输出中出现“第 6 行”后,进程不会被终止)。要在进程给出特定输出后终止它,可以使用

mylongrunningtool | stdbuf -o0 egrep '(term1|term2)' >&-

工作原理如下:

>&-关闭stdout,因此任何写入尝试都将导致错误。

egrep '(term1|term2)'丢弃除包含关键字的行之外的所有输出,在此示例中为“term1”或“term2”。

stdbuf -o0禁用输出缓冲egrep

一旦在 的输出中遇到 关键字之一mylongrunningtoolegrep就会尝试将其传递给stdout并以写入错误终止。结果,SIGPIPE将被发送到 ,mylongrunningtool这将反过来将其杀死。

免责声明:

由于信号是异步的,mylongrunningtool因此可能有机会执行将关键字添加到的语句之后的某些代码stdout,并且基本上不可能保证将执行多少代码。在最坏的情况下,如果mylongrunningtool查询设备驱动程序以进行持续一小时(或永远)的操作,它将再运行一小时(或永远)才会被终止。

此外,SIGPIPE与 不同, 可以被处理SIGKILL。这意味着mylongrunningtool可以忽略信号并继续工作。SIGPIPE但是 的默认处理是终止。

答案3

在这种情况下,包装器脚本可能会有用。这个想法是在后台运行一个程序并将其输出通过管道传输到文件中。
当输出文件满足给定的要求(包含一些字符串或行数)时,终止该程序。

#!/bin/bash

OUTPUT=/path/to/programOutputFile
PROGRAM=/path/to/myprog
$PROGRAM > $OUTPUT  &
PID=$!
echo Program is running under pid: $PID

SEARCH_STRING=bla
MAX_LINES_NUMBER=42
#Every 10 seconds, check requirements
while true; do
   grep $OUTPUT $SEARCH_STRING || break
   test $(wc -l $OUTPUT) -gt MAX_LINES_NUMBER || break 
   sleep 10
done

kill $PID || echo "Killing process with pid $PID failed, try manual kill with -9 argument"

相关内容