7

Допустим, я запускаю процесс, и он выполняет очень длительную процедуру, выводя свой прогресс на стандартный вывод. Есть ли способ завершить процесс автоматически после

  • х строк вывода
    или же
  • определенное ключевое слово было найдено в выводе?

В настоящее время я передаю вывод команды 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`

Любой берущий?

3 ответа3

3

Попробуйте команду 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 , так что вам не нужно беспокоиться о выводе головы.

Наконец, для поиска:

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

Здесь я перенаправил stderr из loop.py , хотя мы уверены, что он не помешает тексту, обработанному head и grep

РЕДАКТИРОВАТЬ

TL; DR: Планировщик ЦП контролирует, насколько интенсивный процесс будет выполняться после завершения вывода head .

После некоторого тестирования я обнаружил, что мое решение, хотя и сокращает выполнение loop.py , не настолько надежно , как это можно сделать. С этими модификациями в моем loop.py конвейерная обработка его вывода к заголовку приводит к:

новый 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 «ы (и я полагаю , все процессы») / выходные потоки помещаются в буфер.

Согласно этому ответу на SO, после того, как получатель (head) завершает работу, канал прерывается, и * только когда отправитель (loop.py) пытается выполнить запись в теперь сломанный канал *, будет отправляться сигнал SIGPIPE.

Поэтому, когда head получил возможность напечатать свой вывод, все это обнаружилось сразу, но только после того, как loop.py продолжил еще 247 строк. (Это связано с планированием процессов.) Более того, после того, как head напечатал свой вывод, но до его завершения, планировщик возобновил loop.py , поэтому еще 250 строк (до 488) были записаны в канал до того, как канал был разорван.

Для лучших результатов мы можем использовать небуферизованный ввод / вывод (в этом случае небуферизованный вывод 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 как unbuffered:

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

Может быть, скрипт-обертка может быть полезен в этом случае. Идея состоит в том, чтобы запустить программу в фоновом режиме и направить ее вывод в файл.
Когда выходной файл удовлетворяет заданным требованиям (содержит некоторую строку или количество строк), закройте программу.

#!/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"
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 умолчанию завершается.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .