假设我正在运行一个进程,它执行了一个非常冗长的过程,将其进度打印到标准输出。有没有办法在执行完以下操作后自动终止该进程:
- 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
在这里,我已将 重定向stderr
到loop.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.h
为stdout
无缓冲:
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
一旦在 的输出中遇到 关键字之一mylongrunningtool
,egrep
就会尝试将其传递给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"