Как запустить команду в цикле, пока не увижу какую-нибудь строку в stdout?
Я уверен, что есть какой-то тривиальный однострочный ключ с perl, ruby, bash, что бы это позволяло мне запускать команду в цикле, пока я не наблюдаю некоторую строку в stdout, а затем остановитесь. В идеале, я хотел бы также захватить stdout, но если он собирается консолировать, этого может быть достаточно.
В настоящее время конкретная окружающая среда - RedHat Linux, но для Mac тоже требуется такая же вещь. Так что-то, generic и * nixy было бы лучше. Не заботьтесь о Windows - предположительно, что * nixy будет работать под cygwin.
UPDATE: Обратите внимание, что "наблюдать за некоторой строкой" я имею в виду, что "stdout содержит некоторую строку", а не "stdout IS some string".
Ответы
Ответ 1
В 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~
Возможно, вы захотите добавить спать.
Ответ 2
Там было множество способов сделать это, первое, что пришло в голову, было:
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
}
Отказ от ответственности: только слегка проверенные, возможно, потребуется выполнить дополнительное экранирование и т.д., если в ваших командах есть много аргументов или строка содержит специальные символы.
РЕДАКТИРОВАТЬ. Основываясь на отзывах от комментария Адама - если вам не нужен вывод по какой-либо причине (т.е. не хотите отображать вывод), вы можете использовать эту более короткую версию, с меньшим использованием backticks и, следовательно, меньшими накладными расходами:
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
}
Ответ 3
Я удивлен, что я не видел краткий однострочный Perl, упомянутый здесь:
perl -e 'do { sleep(1); $_ = `command`; print $_; } until (m/search/);'
Perl - действительно хороший язык для таких вещей. Замените "команду" командой, которую вы хотите повторно запустить. Замените "поиск" тем, что вы хотите найти. Если вы хотите найти что-то с косой чертой, замените m/search/
на строку m#search
на /es#
.
Кроме того, Perl работает на множестве разных платформ, включая Win32, и это будет работать везде, где у вас установлена Perl. Просто измените свою команду соответствующим образом.
Ответ 4
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
... это напечатает несколько хороших точек, чтобы указать прогресс.
Ответ 5
while (/bin/true); do
OUTPUT=`/some/command`
if [[ "x$OUTPUT" != "x" ]]; then
echo $OUTPUT
break
fi
sleep 1
done
Ответ 6
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 никогда не печатает ничего в stdout), однако, если вы используете "echo test", он сразу завершится и, наконец, распечатает тест.
Ответ 7
CONT=1; while [ $CONT -gt 0 ]; do $CMD | tee -a $FILE | grep -q $REGEXP; CONT=$? ; done
Команда tee может захватывать stdout в канале, в то же время передавая данные, а -a
делает это добавлением к файлу, а не переписывает его каждый раз. grep -q
будет return 0
, если было совпадение, 1
в противном случае и ничего не записывает в stdout. $?
- это возвращаемое значение предыдущей команды, поэтому $CONT
будет возвратным значением grep в этом случае.
Ответ 8
Простым способом сделать это будет
until `/some/command`
do
sleep 1
done
Обратные элементы вокруг команды делают тест until
для вывода некоторого вывода, а не для проверки значения выхода команды.