Убить процесс в зависимости от его вывода на стандартный вывод?

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`

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

Махмуд Аль-Кудси
источник
подмножество ключевых слов: superuser.com/questions/402979/…
Сиро Сантилли 新疆 改造 中心 996ICU 六四 事件

Ответы:

4

Попробуйте 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 ...часть error ( ) на самом деле stderr, как демонстрирует выполнение ./loop.py 2> stderr.log | head, поэтому вам не нужно беспокоиться о том, чтобы очистить вывод 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, не настолько надежно, как это можно сделать. С этими модификациями к my 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стандартные потоки ввода / вывода (и, я полагаю, все процессы) буферизируются.

Согласно этому ответу на 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качестве небуферизованного:

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;
}
ladaghini
источник
Но headне убьет процесс, верно? Моя проблема не связана с синтаксическим анализом, а связана со временем процессора. Я не хочу, чтобы этот длительный процесс продолжался.
Махмуд Аль-Кудси
Спасибо за разъяснения. Я думаю, что сочетание этого и timelimitсделает свое дело.
Махмуд Аль-Кудси
Что касается последнего раздела вашего ответа, его проще использовать stdbufдля достижения желаемой буферизации для любой программы, которая может быть запущена из командной строки, не нужно редактировать исходный код.
Дан Ярд
3

Я полагаю, что 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, однако, должна быть прекращена.

Дмитрий Григорьев
источник
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"
bbaja42
источник
Отличный сценарий. Мне пришлось изменить порядок $OUTPUTи $SEARCH_STRINGна моем Mac.
Доминик