2008-09-24 8 views
13

Estoy seguro de que hay un trazador de líneas trivial con perl, ruby, bash, cualquiera que sea que me permita ejecutar un comando en un bucle hasta que observe una cadena en stdout, luego me detengo. Idealmente, me gustaría capturar stdout también, pero si va a la consola, eso podría ser suficiente.¿Cómo ejecuto un comando en un bucle hasta que veo alguna cadena en stdout?

El entorno particular en cuestión en este momento es RedHat Linux, pero a veces también necesita lo mismo en Mac. Entonces algo, genérico y * nixy sería lo mejor. No me importa Windows, presumiblemente una cosa * nixy funcionaría bajo cygwin.

ACTUALIZACIÓN: Tenga en cuenta que por "observar una cadena" me refiero a "stdout contiene una cadena" no "stdout ES una cadena".

Respuesta

13

En 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); 
    } 
} 

Ejemplo:

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

Es posible que desee añadir un sueño allí, sin embargo.

+0

Acepté este porque a) me funcionó de inmediato yb) fue fácil para hackear y agregar algunas cosas (como un contador, etc.). Pero varias de las otras soluciones aquí también se ven bien. –

10

Hay un montón de maneras de hacer esto, el primero que vino a la mente fue:

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

Donde $ cmd es el comando a ejecutar.

Para el gusto de hacerlo, aquí es una versión de la función de golpe, por lo que se puede llamar a esto más fácilmente si es algo que está queriendo invocar desde una consola interactiva sobre una base regular:

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

exención de responsabilidad : solo se ha probado ligeramente, puede ser necesario realizar escapes adicionales, etc. si los comandos tienen muchos argumentos o si la cadena contiene caracteres especiales.

EDITAR: Con base en la retroalimentación de comentarios de Adam - si No necesitan la salida por cualquier razón (es decir, no quieren mostrar la salida), entonces usted puede utilizar esta versión más corta, con menos uso de acentos abiertos y por lo tanto menos sobrecarga:

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

BASH versión de función también:

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

Usted prueba una variable para estar vacía al conectarla a grep en un subconjunto ??? ¡No puedes hablar en serio! A pesar del hecho de que un caparazón puede probar esto fácilmente (cada carcasa puede) y ese código es demasiado complicado, también es ultraperformance. – Mecki

+0

No creo que esté probando si está vacío, está usando grep para determinar si la variable contiene "somestring".Pero podría reducir a la mitad el número de caparazones secundarios moviendo el grep canalizado a la siguiente línea donde está asignado a OUTPUT, luego MIENTRAS estaría probando una variable vacía. –

+0

parece que esto siempre fallará el primero mientras que la prueba como SALIDA está vacía para empezar? –

1
while (/bin/true); do 
    OUTPUT=`/some/command` 
    if [[ "x$OUTPUT" != "x" ]]; then 
    echo $OUTPUT 
    break 
    fi 

    sleep 1 
done 
+0

Entonces, ¿aquí está la cadena para buscar? Si estuviera buscando foo, lo haría: if [["foo $ OUTPUT"! = "Foo"]]; luego ? –

+0

Si "x $ OUTPUT" == "x", entonces $ OUTPUT está vacío. Esto solo comprueba si hay * cualquier * salida. –

1

EDITAR: Mi respuesta original fue asumir que "una cadena" significa "cualquier cadena". Si necesita buscar uno específico, Perl es probablemente su mejor opción, ya que casi nada puede vencer a Perl en lo que respecta a la coincidencia de REGEX.

Sin embargo, si no puede usar Perl por algún motivo (puede esperar que Perl esté presente en la mayoría de las distribuciones de Linux, pero nadie obliga al usuario a instalarlo, aunque Perl puede no estar disponible), puede hacerlo con la ayuda de grep. Sin embargo, algunas de las soluciones grep que he visto hasta ahora son subóptimas (son más lentas de lo que sería necesario). En ese caso, yo preferiría hacer lo siguiente:

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

Reemplazar COMANDO con el comando de hecho para correr y SOME_STRING con la cadena a buscar. Si SOME_STRING se encuentra en la salida de COMMAND, el ciclo se detendrá e imprimirá el resultado donde se encontró SOME_STRING.

respuesta original:

Probablemente no

la mejor solución (no soy un buen programador bash), pero funcionará :-P

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

basta con sustituir XXXX con su llamada de comando, por ejemplo, intente utilizar "echo" y nunca volverá (ya que el eco nunca imprime nada en stdout); sin embargo, si usa "echo test" terminará de inmediato y finalmente imprimirá la prueba.

+0

[["e $ MATCH" == "e"]] es mucho menos claro que [[-n "$ MATCH"]] –

1
CONT=1; while [ $CONT -gt 0 ]; do $CMD | tee -a $FILE | grep -q $REGEXP; CONT=$? ; done 

El comando tee puede capturar la salida estándar en un tubo mientras que todavía pasar los datos en, y -a hace añadir al archivo en lugar de sobrescribir cada vez. grep -qreturn 0 si hubiera una coincidencia, 1 de lo contrario y no escribe nada en stdout. $? es el valor de retorno del comando anterior, por lo que $CONT será el valor de retorno de grep en este caso.

+0

Mucho más simple: hasta $ CMD | tee -a $ ARCHIVO | grep -q $ REGEXP; dormir 1; done ... El shell ejecutará la tubería repetidamente hasta que el estado de salida del grep sea exitoso. No hay necesidad de $ CONT o referencias explícitas a $ ?. –

+0

Ah, y no estoy seguro de que '-q' sea portátil; POSIX ordena '-s' para salir con el estado 0 (encontrado) o 1 (no encontrado). –

0

Una forma sencilla de hacer esto sería

until `/some/command` 
do 
    sleep 1 
done 

Los acentos abiertos todo el comando de hacer la prueba until por alguna salida que se devuelve en lugar de probar el valor de salida del comando.

+1

Solo las pruebas salen del estado, no stdout; por lo tanto, no responde la pregunta. –

+1

Te perdiste los backticks alrededor del comando que cambian lo que hasta evalúa. Añadiré una edición para aclarar esto. –

+1

El ciclo until ejecuta/some/command y ejecuta su salida, probando el estado de salida. Esto está inherentemente roto. –

4

grep -c 99999 imprimirá 99999 líneas de contexto para el partido (supongo que esto será suficiente):

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

o

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

... esto va a imprimir algunos puntos agradables para indicar el progreso .

7

Me sorprende que no he visto una breve Perl de una sola línea se ha mencionado aquí:

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

Perl es un lenguaje muy agradable para cosas como esta. Reemplace "comando" con el comando que desea ejecutar repetidamente. Reemplaza "buscar" con lo que deseas buscar. Si desea buscar algo con una barra, reemplace m/search/ con m#search cadena con /es#.

Además, Perl se ejecuta en muchas plataformas diferentes, incluyendo Win32, y esto funcionará donde sea que tenga una instalación de Perl. Solo cambia tu comando apropiadamente.

Cuestiones relacionadas