2008-09-24 9 views
13

Je suis sûr qu'il y a un doublage trivial avec perl, ruby, bash, tout ce qui me permettrait d'exécuter une commande dans une boucle jusqu'à ce que j'observe une chaîne dans stdout, puis arrêtez. Idéalement, j'aimerais aussi capturer stdout, mais si ça va se consoler, ça suffira.Comment exécuter une commande dans une boucle jusqu'à ce que je vois une chaîne dans stdout?

L'environnement particulier en question à l'heure actuelle est RedHat Linux mais nécessite parfois la même chose sur Mac. Donc quelque chose, générique et * nixy serait le meilleur. Ne vous souciez pas de Windows - probablement une chose * nixy fonctionnerait sous cygwin. MISE À JOUR: Notez que par "observez une chaîne", je veux dire "stdout contient une chaîne" et non "stdout EST une chaîne".

Répondre

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

Exemple:

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

Vous pouvez ajouter un sommeil là-dedans, cependant.

+0

J'ai accepté celui-ci parce que a) ça a fonctionné pour moi tout de suite et b) c'était facile pirater pour ajouter quelques choses (comme un compteur, etc). Mais plusieurs des autres solutions ici semblent bien aussi. –

10

Il y a un tas de façons de faire, les premiers qui viennent à l'esprit était:

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

Où cmd $ est votre commande à exécuter.

Pour le fun, voici une version de fonction BASH, donc vous pouvez appeler cela plus facilement si elle est quelque chose que vous êtes désireux d'invoquer d'un shell interactif sur une base régulière:

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

Avertissement : seulement légèrement testé, peut-être besoin de faire des échappements supplémentaires, etc. si vos commandes ont beaucoup d'arguments ou si la chaîne contient des caractères spéciaux.

EDIT: D'après les commentaires de commentaire d'Adam - si vous ne pas besoin de la sortie pour une raison quelconque (ne pas afficher la sortie), vous pouvez utiliser cette version plus courte, avec moins d'utilisation de guillemets obliques et donc moins de frais généraux:

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

version fonction BASH aussi:

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

Vous testez une variable pour être vide en la redirigeant vers grep dans un sous-shell ??? Tu ne peux pas être sérieux! Malgré le fait qu'un shell puisse tester cela facilement (chaque coque peut) et que ce code soit bien trop compliqué, il est aussi ultra performant. – Mecki

+0

Je ne pense pas qu'il teste s'il est vide, il utilise grep pour déterminer si la variable contient "somestring".Mais il pourrait réduire le nombre de sous-shells de moitié en déplaçant le grep canalisé à la ligne suivante où il est assigné à OUTPUT, alors que WHILE testerait une variable vide. –

+0

semble que cela échouera toujours le premier tout test alors que OUTPUT est vide pour commencer? –

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

    sleep 1 
done 
+0

Donc, x voici la chaîne à rechercher? Si je cherchais foo, je ferais: if [["foo $ OUTPUT"! = "Foo"]]; puis ? –

+0

Si "x $ OUTPUT" == "x", alors $ OUTPUT est vide. Cela vérifie juste s'il y a une sortie * any *. –

1

EDIT: Ma réponse originale supposait que "une chaîne" signifie "n'importe quelle chaîne". Si vous en recherchez un spécifique, Perl est probablement votre meilleure option, puisque presque rien ne peut battre Perl quand il s'agit de faire correspondre REGEX. Cependant, si vous ne pouvez pas utiliser Perl pour une raison quelconque (vous pouvez vous attendre à ce que Perl soit présent dans la plupart des distributions Linux, mais personne ne force l'utilisateur à l'installer, bien que Perl ne soit pas disponible) avec l'aide de grep. Cependant, certaines des solutions grep que j'ai vues jusqu'ici sont sous-optimales (elles sont plus lentes que nécessaire). Dans ce cas, je préfère faire ce qui suit:

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

Remplacer COMMANDE avec la commande fait courir et SOME_STRING avec la chaîne de recherche. Si SOME_STRING est trouvé dans la sortie de COMMAND, la boucle s'arrêtera et affichera la sortie où SOME_STRING a été trouvé.

RÉPONSE ORIGINAL:

Probablement pas la meilleure solution (je ne suis pas bon programmeur bash), mais il fonctionnera :-P

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

Il suffit de remplacer XXXX votre appel de commande, par exemple essayez d'utiliser "echo" et il ne reviendra jamais (comme echo n'imprime jamais rien à stdout), mais si vous utilisez "echo test" il se terminera à la fois et finalement imprimer test.

+0

[["e $ MATCH" == "e"]] est beaucoup moins clair que [[-n "$ MATCH"]] –

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

La commande tee peut capturer stdout dans une conduite tout en passant les données sur, et -a rend append au fichier au lieu de l'écraser à chaque fois. grep -qreturn 0 s'il y avait une correspondance, 1 sinon et n'écrit rien à stdout. $? est la valeur de retour de la commande précédente, donc $CONT sera la valeur de retour de grep dans ce cas.

+0

Beaucoup plus simple: jusqu'à $ CMD | tee -a $ FICHIER | grep -q $ REGEXP; fais dormir 1; done ... Le shell exécutera le pipeline plusieurs fois jusqu'à ce que le statut de sortie du grep soit réussi. Pas besoin de $ CONT ou références explicites à $ ?. –

+0

Oh, et je ne suis pas sûr que '-q' soit portable; POSIX impose '-s' pour quitter avec le statut 0 (trouvé) ou 1 (non trouvé). –

0

Une façon simple de le faire serait

until `/some/command` 
do 
    sleep 1 
done 

Les guillemets obliques autour de la commande font le test until pour une sortie à retourner plutôt que de tester la valeur de sortie de la commande.

+1

Seuls les tests sortent de l'état, pas de sortie standard; donc ne répond pas à la question. –

+1

Vous avez manqué les accolades autour de la commande qui changent ce que l'on évalue. Je vais ajouter une modification pour clarifier cela. –

+1

La boucle until exécute/some/command et exécute sa sortie, testant l'état de sortie. Ceci est intrinsèquement brisé. –

4

grep -c 99999 imprimera 99999 lignes de contexte pour le match (je suppose que ce sera suffisant):

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

ou

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

... ce imprimera quelques beaux points pour indiquer la progression .

7

Je suis surpris que je ne l'ai pas vu un bref Perl one-liner mentionné ici:

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

Perl est un langage très agréable pour des trucs comme ça. Remplacer "commande" avec la commande que vous souhaitez exécuter de façon répétée. Remplacer "recherche" avec ce que vous voulez rechercher. Si vous voulez rechercher quelque chose avec une barre oblique, remplacez m/search/ par m#search avec /es#.

De plus, Perl fonctionne sur beaucoup de plates-formes différentes, y compris Win32, et cela fonctionnera partout où vous avez une installation Perl. Changez juste votre commande de manière appropriée.

Questions connexes