Как запустить команду в цикле, пока не увижу строку в stdout? - PullRequest
14 голосов
/ 24 сентября 2008

Я уверен, что есть некоторые тривиальные однострочные с perl, ruby, bash, которые позволят мне выполнить команду в цикле, пока я не увижу некоторую строку в stdout, а затем остановлюсь. В идеале, я хотел бы также захватить стандартный вывод, но если он собирается утешить, этого может быть достаточно.

В настоящее время речь идет о RedHat Linux, но иногда на Mac требуется то же самое. Так что что-то, generic и * nixy было бы лучше. Не заботьтесь о Windows - возможно, что * nixy будет работать под Cygwin.

ОБНОВЛЕНИЕ: Обратите внимание, что под "наблюдать некоторую строку" я имею в виду "stdout содержит некоторую строку", а не "stdout IS некоторая строка".

Ответы [ 8 ]

13 голосов
/ 24 сентября 2008

В Perl:

#!/usr/local/bin/perl -w

if (@ARGV != 2)
{
    print "Usage: watchit.pl <cmd> <str>\n";
    exit(1);
}

$cmd = $ARGV[0];
$str = $ARGV[1];

while (1)
{
    my $output = `$cmd`;
    print $output; # or dump to file if desired
    if ($output =~ /$str/)
    {
        exit(0);
    }
}

Пример:

[bash$] ./watchit.pl ls stop
watchit.pl
watchit.pl~
watchit.pl
watchit.pl~
... # from another terminal type "touch stop"
stop 
watchit.pl
watchit.pl~

Возможно, вы захотите добавить туда сон.

11 голосов
/ 24 сентября 2008

Существует множество способов сделать это, первое, что пришло в голову:

OUTPUT=""; 
while [ `echo $OUTPUT | grep -c somestring` = 0 ]; do 
  OUTPUT=`$cmd`; 
done

Где $ cmd - ваша команда для выполнения.

Для примера, вот версия функции BASH, так что вы можете вызвать ее проще, если вы хотите регулярно вызывать ее из интерактивной оболочки:

function run_until () {
  OUTPUT="";
  while [ `echo $OUTPUT | grep -c $2` = 0 ]; do
    OUTPUT=`$1`;
    echo $OUTPUT;
  done
}

Отказ от ответственности: только слегка проверено, может потребоваться выполнить дополнительное экранирование и т. Д., Если у ваших команд много аргументов или строка содержит специальные символы.

РЕДАКТИРОВАТЬ : Основываясь на отзывах из комментария Адама - если вам не нужен вывод по какой-либо причине (т.е. вы не хотите отображать вывод), тогда вы можете используйте эту более короткую версию, с меньшим количеством обратных ссылок и, следовательно, с меньшими накладными расходами:

OUTPUT=0; 
while [ "$OUTPUT" = 0 ]; do 
  OUTPUT=`$cmd | grep -c somestring`;
done

Версия функции BASH также:

function run_until () {
  OUTPUT=0; 
  while [ "$OUTPUT" = 0 ]; do 
    OUTPUT=`$1 | grep -c $2`; 
  done
}
7 голосов
/ 02 октября 2008

Я удивлен, что не видел краткого однострочного Perl, упомянутого здесь:

perl -e 'do { sleep(1); $_ = `command`; print $_; } until (m/search/);'

Perl - действительно хороший язык для подобных вещей. Замените «команда» на команду, которую вы хотите запустить повторно. Замените «поиск» тем, что вы хотите найти. Если вы хотите найти что-то с косой чертой, замените m/search/ на m#search строку на /es#.

Кроме того, Perl работает на множестве различных платформ, включая Win32, и это будет работать везде, где у вас установлена ​​Perl. Просто измените свою команду соответствующим образом.

5 голосов
/ 25 сентября 2008

grep -c 99999 напечатает 99999 строк контекста для соответствия (я полагаю, этого будет достаточно):

while true; do /some/command | grep expected -C 99999 && break; done

или

until /some/command | grep expected -C 9999; do echo -n .; done

... это напечатает несколько хороших точек, указывающих на прогресс.

1 голос
/ 25 сентября 2008
CONT=1; while [ $CONT -gt 0 ]; do $CMD | tee -a $FILE | grep -q $REGEXP; CONT=$? ; done

Команда tee может перехватить стандартный вывод в канале, все еще передавая данные, и -a заставляет его добавлять файл, а не перезаписывать его каждый раз. grep -q будет return 0, если было совпадение, 1 в противном случае и ничего не записывает в стандартный вывод. $? - это возвращаемое значение предыдущей команды, поэтому $CONT будет возвращаемым значением grep в этом случае.

1 голос
/ 24 сентября 2008

EDIT: Мой первоначальный ответ предполагал, что «некоторая строка» означает «любая строка». Если вам нужно найти конкретный вариант, Perl, вероятно, ваш лучший вариант, так как почти ничего не может сравниться с Perl, когда дело доходит до соответствия REGEX.

Однако, если вы не можете использовать Perl по какой-либо причине (вы можете ожидать, что Perl присутствует в большинстве дистрибутивов Linux, но никто не заставляет пользователя устанавливать его, хотя Perl может быть недоступен), вы можете сделать это с помощью помощь grep. Однако некоторые из решений grep, которые я видел до сих пор, являются неоптимальными (они работают медленнее, чем необходимо). В этом случае я бы предпочел сделать следующее:

MATCH=''; while [[ "e$MATCH" == "e" ]]; do MATCH=`COMMAND | grep "SOME_STRING"`; done; echo $MATCH

Замените COMMAND на фактическую команду для запуска и SOME_STRING на строку для поиска. Если SOME_STRING найден в выходных данных COMMAND, цикл остановится и напечатает выходные данные, где был найден SOME_STRING.

ОРИГИНАЛЬНЫЙ ОТВЕТ:

Возможно, не лучшее решение (я не очень хороший программист Bash), но оно будет работать: -P

RUN=''; while [[ "e$RUN" == "e" ]]; do RUN=`XXXX`; done ; echo $RUN

Просто замените XXXX на ваш командный вызов, например попробуйте использовать " echo ", и он никогда не вернется (поскольку echo никогда ничего не печатает на стандартный вывод), однако, если вы используете " echo test ", он сразу прекратит работу и, наконец, выведет тест .

1 голос
/ 24 сентября 2008
while (/bin/true); do
  OUTPUT=`/some/command`
  if [[ "x$OUTPUT" != "x" ]]; then
    echo $OUTPUT
    break
  fi

  sleep 1
done
0 голосов
/ 25 сентября 2008

Простой способ сделать это будет

until `/some/command`
do
  sleep 1
done

Обратные пометки вокруг команды делают тест until для некоторого вывода, который должен быть возвращен, вместо проверки значения выхода команды.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...