2008-09-24 9 views
13

Ich bin mir sicher, es gibt ein triviales Einzeiler mit Perl, Ruby, Bash was auch immer das würde ich einen Befehl in einer Schleife ausführen lassen, bis ich eine Zeichenfolge in stdout beobachte, dann hör auf. Im Idealfall würde ich auch gerne Stdout aufnehmen, aber wenn es zur Konsole wird, könnte das ausreichen.Wie führe ich einen Befehl in einer Schleife aus, bis ich eine Zeichenfolge in stdout sehe?

Die betreffende Umgebung im Moment ist RedHat Linux, aber das gleiche Ding auf Mac manchmal auch brauchen. Also etwas, generisches und * nixy wäre am besten. Kümmern Sie sich nicht um Windows - vermutlich würde ein * nixiges Ding unter Cygwin funktionieren.

UPDATE: Beachten Sie, dass durch "beobachten Sie einige Zeichenfolge" ich meine "stdout enthält einige Zeichenfolge" nicht "stdout ist einige Zeichenfolge".

Antwort

13

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

Beispiel:

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

Sie könnten in ein Schlaf hinzufügen möchten, though.

+0

Ich akzeptierte dieses, weil a) es für mich sofort funktionierte und b) es einfach war zu hacken, um ein paar Dinge hinzuzufügen (wie ein Zähler, etc). Aber einige der anderen Lösungen sehen auch gut aus. –

10

eine Reihe von Möglichkeiten gibt, dies zu tun, das erste, das in den Sinn kam, war:

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

Wo $ cmd Ihren Befehl auszuführen ist.

Für das Heck von ihm, hier ist ein BASH Funktionsversion, so können Sie dies einfach mehr anrufen, wenn es etwas, das Sie von einer interaktiven Shell auf regelmäßig aufrufen wollen, sind:

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

Haftungsausschluss : nur leicht getestet, müssen möglicherweise einige zusätzliche Escaping etc., wenn Ihre Befehle viele Argumente haben oder die Zeichenfolge enthält spezielle Zeichen.

EDIT: Basierend auf dem Feedback von Adams Kommentar - wenn Sie den Ausgang aus irgendeinem Grund nicht müssen tun (dh wollen nicht die Ausgabe angezeigt werden), dann können Sie diese kürzere Version verwenden, mit weniger Verwendung von Backticks und daher weniger Aufwand:

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

BASH Funktion Version auch:

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

Sie testen eine Variable für leer, indem Sie es in eine Sub-Shell grep ??? Das kann nicht dein Ernst sein! Trotz der Tatsache, dass eine Shell dies leicht testen kann (jede Shell kann) und dieser Code ist viel zu kompliziert, ist es auch ultra inperformant. – Mecki

+0

Ich glaube nicht, dass er testet, wenn es leer ist, er verwendet grep um festzustellen, ob die Variable "somestring" enthält.Aber er konnte die Anzahl der Sub-Shells um die Hälfte reduzieren, indem er den piped grep in die folgende Zeile verschiebt, wo er OUTPUT zugewiesen wird. Dann würde WHILE auf eine leere Variable testen. –

+0

scheint so, wird immer den ersten Test während immer fehlschlagen, da OUTPUT leer ist, mit zu beginnen? –

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

    sleep 1 
done 
+0

Also, x ist hier die Zeichenfolge, nach der gesucht werden soll? Wenn ich nach foo suchen würde, würde ich tun: wenn [["foo $ OUTPUT"! = "Foo"]]; dann ? –

+0

Wenn "x $ OUTPUT" == "x", dann ist $ OUTPUT leer. Dies prüft nur, ob * irgendeine * Ausgabe vorliegt. –

1

EDIT: Meine ursprüngliche Antwort ging davon aus, dass "einige Zeichenfolge" bedeutet "beliebige Zeichenfolge". Wenn Sie nach einem bestimmten suchen, ist Perl wahrscheinlich Ihre beste Option, da fast nichts Perl schlagen kann, wenn es um REGEX-Abgleich geht.

Wenn Sie jedoch Perl aus irgendeinem Grund nicht verwenden können (Sie können erwarten, dass Perl in den meisten Linux-Distributionen vorhanden ist, aber niemand einen Benutzer zur Installation zwingt, obwohl Perl möglicherweise nicht verfügbar ist), können Sie es tun mit Hilfe von Grep. Einige der grep-Lösungen, die ich bisher gesehen habe, sind jedoch suboptimal (sie sind langsamer als nötig). In diesem Fall würde ich eher die folgende:

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

ersetzen COMMAND mit dem Befehl tatsächlich auszuführen und some_string mit der Zeichenfolge zu suchen. Wenn SOME_STRING in der Ausgabe von COMMAND gefunden wird, stoppt die Schleife und druckt die Ausgabe, wo SOME_STRING gefunden wurde.

ORIGINAL ANTWORT:

Wahrscheinlich nicht die beste Lösung (ich bin nicht gut bash Programmierer), aber es wird mit Ihrem Befehlsaufruf :-P

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

Gerade XXXX ersetzen arbeiten, z.B. versuchen Sie es mit "echo" und es wird nie zurückkehren (da echo nie etwas auf stdout druckt), aber wenn Sie "echotest" verwenden, wird es sofort beenden und schließlich Test ausdrucken.

+0

[["e $ MATCH" == "e"]] ist viel weniger klar als [[-n "$ MATCH"]] –

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

Der Abschlag Befehl kann stdout in einem Rohr erfassen, während die Daten noch auf vorbei, und -a macht es in die Datei anhängen, anstatt sie jedes Mal zu überschreiben. grep -q wird return 0 wenn es eine Übereinstimmung gab, 1 sonst und nichts auf stdout schreiben. $? ist der Rückgabewert des vorherigen Befehls, daher ist in diesem Fall $CONT der Rückgabewert von grep.

+0

Viel einfacher: bis $ CMD | tee -a $ DATEI | grep -q $ REGEXP; Schlaf 1; done ... Die Shell wird die Pipeline wiederholt ausführen, bis der Exit-Status von grep erfolgreich ist. Keine Notwendigkeit für $ CONT oder explizite Referenzen zu $ ​​?. –

+0

Oh, und ich bin mir nicht sicher, dass '-q' tragbar ist; POSIX gibt '-s' für den Ausgang mit Status 0 (gefunden) oder 1 (nicht gefunden) aus. –

0

Ein einfacher Weg, dies zu tun, wäre

until `/some/command` 
do 
    sleep 1 
done 

Die Backticks um den Befehl, um die until Test machen für einige Ausgabe eher zurückgeführt werden, als den Ausgang Wert des Befehls zu testen.

+1

Nur Tests beendet den Status, nicht stdout; so beantwortet die Frage nicht. –

+1

Sie haben die Backticks um den Befehl herum verpasst, die ändern, was der bis bewertet. Ich werde eine Bearbeitung hinzufügen, um dies zu verdeutlichen. –

+1

Die Schleife bis/lies/command führt die Ausgabe aus und testet den Exit-Status. Dies ist inhärent gebrochen. –

4

grep -c 99999 werden 99999 Zeilen Kontext für das Spiel drucken (ich nehme an das wird genug sein, um):

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

oder

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

... das wird einige schöne Punkte drucken Fortschritte, um anzuzeigen, .

7

Ich bin überrascht, ich habe nicht eine kurze Perl Einzeiler hier erwähnt gesehen:

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

Perl ist eine sehr schöne Sprache für Sachen. Ersetzen Sie "command" durch den Befehl, den Sie wiederholt ausführen möchten. Ersetzen Sie "Suche" mit dem, wonach Sie suchen möchten. Wenn Sie mit einem Schrägstrich nach etwas suchen möchten, ersetzen Sie m/search/ durch m#search Zeichenfolge mit /es#.

Auch Perl läuft auf vielen verschiedenen Plattformen, einschließlich Win32, und dies funktioniert überall dort, wo Sie eine Perl-Installation haben. Ändern Sie einfach Ihren Befehl entsprechend.

Verwandte Themen