2009-07-03 2 views
6

Ich schreibe Shell-Skripte, wo ziemlich regelmäßig einige Sachen in eine Datei geschrieben wird, nach denen eine Anwendung ausgeführt wird, die diese Datei liest. Ich finde, dass die Netzwerklatenz durch unsere Firma sehr unterschiedlich ist, so dass eine einfache sleep 2 zum Beispiel nicht robust genug sein wird.Wie mit NFS Latenz in Shell-Skripten umgehen

Ich versuchte, ein (konfigurierbar) Timeout-Schleife wie folgt zu schreiben: für test="[ -e $somefilename ]"

waitLoop() 
{ 
    local timeout=$1 
    local test="$2" 

    if ! $test 
    then 
     local counter=0 
     while ! $test && [ $counter -lt $timeout ] 
     do 
     sleep 1 
     ((counter++)) 
     done 

     if ! $test 
     then 
     exit 1 
     fi 
    fi 
} 

Dies funktioniert. Das Testen der Existenz ist jedoch nicht genug, manchmal muss ich testen, ob eine bestimmte Zeichenfolge in die Datei geschrieben wurde. Ich habe versucht test="grep -sq \"^sometext$\" $somefilename", aber das hat nicht funktioniert. Kann mir jemand sagen warum?

Gibt es andere, weniger ausführliche Optionen, um einen solchen Test durchzuführen?

+0

Wäre es möglich, einfach auf die Datei zu laufen, und nur die Ausgabe von Schwanz (d. H. Die letzten Zeilen) zu überprüfen? Das geht davon aus, dass Tail cleverer ist als das Skript, um zu überprüfen, wann sich eine Datei ändert (Und die Wahrscheinlichkeit ist, dass der Tail es richtiger macht) – nos

+0

Ich denke, der richtige Ansatz hängt davon ab, was "ein paar Sachen" sind und was die "Anwendung" ist. Kannst du das wirkliche Problem, das du lösen willst, genauer beschreiben? Die geschriebene Frage ist ein wenig vage; Es kann ganz andere Ansätze geben, an die Sie nicht gedacht haben. –

+0

Siehe meine bearbeitete Antwort. –

Antwort

1

Sie können Ihre Testvariable auf diese Weise festgelegt:

test=$(grep -sq "^sometext$" $somefilename) 

Der Grund Ihrer grep ist nicht funktioniert, dass Zitate sind wirklich schwer in Argumente zu übergeben.

if ! eval $test 
0

Ich würde sagen, die Weg, um nach einer Zeichenkette in einer Textdatei zu suchen ist Grep.

Was ist Ihr genaues Problem damit?

Sie können auch Ihre NFS-Mount-Parameter anpassen, um das Grundproblem loszuwerden. Eine Synchronisierung könnte auch helfen. Siehe NFS-Dokumente.

+0

Ich habe keine Möglichkeit, Mount-Parameter zu ändern. Mein Skript sollte robust genug sein, um mit der Latenz umzugehen. Das Problem liegt nicht bei Grep selbst, sondern eher darin, dass der Grep-Test nicht so evaluiert wird, wie ich es erwarten würde. –

+0

Okay, wenn du deine Umgebung nicht ändern kannst, ist das ein Problem. Anyways, versuchte eine '' Synchronisierung '' nach dem Ändern der Datei? Laut NFS docs sollte es tun (solange es sysnc.2 aufruft). – TheBonsai

0

Wenn Sie waitLoop in einem "if" verwenden möchten, können Sie den "exit" in einen "return" ändern, damit der Rest des Skripts die Fehlersituation verarbeiten kann (es gibt nicht einmal eine Nachricht) an den Benutzer über was gescheitert ist, bevor das Skript andernfalls stirbt).

Das andere Problem ist die Verwendung von "$ test", um einen Befehl zu halten bedeutet, dass Sie keine Shell-Erweiterung erhalten, wenn Sie tatsächlich ausführen, nur zu bewerten. Wenn Sie also test = "grep \ foo \" \ "bar baz \" angeben, sucht die Datei nicht nach der dreistelligen Zeichenfolge foo in der Datei mit der siebenstelligen Namensleiste baz, sondern nach der fünf Zeichenfolge "foo" in der neun Char-Datei "bar baz".

So können Sie entweder entscheiden Sie die Shell Magie nicht benötigen, und stellen Sie Test = ‚grep -SQ^sometext $ somefilename‘, oder Sie können die Shell erhalten zu den Griff zu zitieren ausdrücklich mit so etwas wie:

if /bin/sh -c "$test" 
then 
    ... 
0

Versuchen Sie es mit der Datei-Änderungszeit zu erfassen, wenn es ohne Öffnen geschrieben steht: Sie werden eval verwenden müssen. So etwas wie

old_mtime=`stat --format="%Z" file` 
# Write to file. 
new_mtime=$old_mtime 
while [[ "$old_mtime" -eq "$new_mtime" ]]; do 
    sleep 2; 
    new_mtime=`stat --format="%Z" file` 
done 

Dies funktioniert jedoch nicht, wenn mehrere Prozesse versuchen, auf die Datei zur gleichen Zeit zuzugreifen.

0

Ich hatte gerade genau das gleiche Problem. Ich habe einen ähnlichen Ansatz für die Wartezeit verwendet, die Sie in Ihr OP einbeziehen. Ich habe jedoch auch eine Überprüfung der Dateigröße vorgenommen. Ich habe meinen Timeout-Timer zurückgesetzt, wenn die Datei seit der letzten Überprüfung größer geworden ist. Die Dateien, die ich schreibe, können ein paar Gigs sein, also brauchen sie eine Weile, um über NFS zu schreiben.

Dies kann für Ihren speziellen Fall Overkill sein, aber ich hatte auch meinen Schreibprozess einen Hash der Datei berechnen, nachdem es fertig geschrieben wurde. Ich habe md5 benutzt, aber sowas wie crc32 würde auch funktionieren. Dieser Hash wurde vom Schreiber an die (mehrere) Leser gesendet, und der Leser wartet, bis a) die Dateigröße aufhört anzusteigen und b) der (frisch berechnete) Hash der Datei mit dem vom Schreiber gesendeten Hash übereinstimmt.

+0

danke. Wie übertragen Sie den MD5-Hash? Und würde eine Warteschleife für diesen Hash nicht ausreichen? –

+0

Wir verwenden ein Standard-Netzwerk-Nachrichtenprotokoll (POE für Perl; aber JMS oder AMQP sind für diese Funktionalität identisch). In unserem Fall befinden sich der Schreiber und die Leser auf verschiedenen Rechnern, und der Schreiber überträgt den Hash als Teil der Nachricht, die anzeigt, dass der Schreibvorgang abgeschlossen ist. Die Leser nehmen die Nachricht auf, warten darauf, dass ihre Ansicht der Datei nicht mehr wächst, und überprüfen dann den Hash. –

+0

Und, ja, eine Warteschleife auf dem Hash würde auch funktionieren - aber die Berechnung des Hash ist relativ teuer im Vergleich zur Überprüfung der Dateigröße, also mache ich die einfache/billige Sache als "Gatekeeper" für die schwere/teure Sache . –

0

Wir haben ein ähnliches Problem, aber aus verschiedenen Gründen. Wir lesen eine s-Datei, die an einen SFTP-Server gesendet wird. Der Computer, auf dem das Skript ausgeführt wird, ist nicht der SFTP-Server.

Was ich getan habe, ist es in Cron eingerichtet (obwohl eine Schleife mit einem Schlaf würde auch funktionieren), um ein cksum der Datei zu tun. Wenn das alte cksum mit dem aktuellen cksum übereinstimmt (die Datei hat sich für die festgelegte Zeitspanne nicht geändert), wissen wir, dass die Schreibvorgänge abgeschlossen sind, und übertragen die Datei.

Um besonders sicher zu sein, überschreiben wir niemals eine lokale Datei, bevor eine Sicherung erstellt wird, und übertragen nur dann, wenn die entfernte Datei zwei cksums in einer Zeile hat, die übereinstimmen und cksum nicht mit der lokalen Datei übereinstimmt.

Wenn Sie Code-Beispiele benötigen, bin ich sicher, dass ich sie ausgraben kann.

0

Die Shell teilte Ihr Prädikat in Wörter auf. Schnappen sie alle mit [email protected] wie in dem folgenden Code:

#! /bin/bash 

waitFor() 
{ 
    local tries=$1 
    shift 
    local predicate="[email protected]" 

    while [ $tries -ge 1 ]; do 
    ((tries--)) 

    if $predicate >/dev/null 2>&1; then 
     return 
    else 
     [ $tries -gt 0 ] && sleep 1 
    fi 
    done 

    exit 1 
} 

pred='[ -e /etc/passwd ]' 
waitFor 5 $pred 
echo "$pred satisfied" 

rm -f /tmp/baz 
(sleep 2; echo blahblah >>/tmp/baz) & 
(sleep 4; echo hasfoo >>/tmp/baz) & 

pred='grep ^hasfoo /tmp/baz' 
waitFor 5 $pred 
echo "$pred satisfied" 

Ausgang:

 
$ ./waitngo 
[ -e /etc/passwd ] satisfied 
grep ^hasfoo /tmp/baz satisfied 

Schade, das Typoskript ist nicht so interessant wie es in Echtzeit zu beobachten.

-1

Ok ... das ist ein bisschen whacky ...

Wenn Sie die Kontrolle über die Datei haben: Sie möglicherweise in der Lage ein ‚Named Pipe‘ hier zu schaffen. So (je nachdem, wie das Schreibprogramm funktioniert) können Sie die Datei in einer synchronisierten Art und Weise überwachen.

Am einfachsten:

Erstellen Sie die Named Pipe:

mkfifo file.txt 

den synchronisierten Empfänger ein:

while : 
do 
    process.sh < file.txt 
end 

Erstellen Sie einen Test Absender:

echo "Hello There" > file.txt 

Die 'process.sh' ist wo Ihre Logik geht: Dies wird blockiert, bis der Absender seine Ausgabe geschrieben hat. In der Theorie braucht das Schreiberprogramm keine Änderung ....

WARNUNG: Wenn der Empfänger aus irgendeinem Grund nicht läuft, können Sie den Absender blockieren!

Nicht sicher, dass es Ihre Anforderung hier entspricht, aber möglicherweise einen Blick wert ist.

Oder um synchronisiert zu vermeiden, versuche 'lsof'?

http://en.wikipedia.org/wiki/Lsof

Unter der Annahme, dass Sie nur aus der Datei lesen wollen, wenn nichts anderes ist schriftlich (dh hat der Schreibvorgang beendet) - Sie können überprüfen, ob nichts anderes Datei-Handle zu ihm hat?

+0

Gute Idee, aber ein Punkt zu beachten ist, dass Named Pipes nicht funktionieren, wenn der Sender und Empfänger auf zwei verschiedenen Hosts sind, auch wenn sie die gleichen NFS-Mount-Punkte haben. – dogbane

+0

doh! Sollte den Titel richtig gelesen haben - yup funktioniert nicht über NFS Mounts! – monojohnny