2010-12-20 21 views
18

(möglicherweise im Zusammenhang mit Do some programs not accept process substitution for input files?)Bash Prozess Substitution und Synchronisieren

In einigen Bash Unit-Test-Skripte ich die folgenden Trick mit und Anzeige stdout und stderr eines Befehls zu protokollieren:

command > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2) 

Dieser Prozess erzeugt eine Ausgabe zu stdout, so dass die Datei $stdoutF einige Daten erhält. Dann laufe ich einen anderen Befehl, der alle Daten, nicht ausgibt:

diff -r "$source" "$target" > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2) 
jedoch

, es sieht nicht wie dieser Prozess immer beendet erfolgreich, bevor der Test für Leere ausgeführt wird (mit shunit-ng):

assertNull 'Unexpected output to stdout' "$(<"$stdoutF")" 

In einem 100-Lauf-Test ist dies 25 Mal fehlgeschlagen.

Sollte es ausreichend sein, sync aufrufen, bevor die Datei für die Leere Prüfung:

sync 
assertNull 'Unexpected output to stdout' "$(<"$stdoutF")" 

... und/oder sollte es funktionieren, indem die Reihenfolge der Befehle zwingen:

diff -r "$source" "$target" \ 
> >(tee "${stdoutF}"; assertNull 'Unexpected output to stdout' "$(<"$stdoutF")") 
2> >(tee "${stderrF}" >&2) 

. .. und/oder ist es möglich, tee es irgendwie zu assertNull direkt anstelle einer Datei?

Aktualisieren: sync ist nicht die Antwort - Siehe Gilles Antwort unten.

Update 2: Diskussion weiter zu Save stdout, stderr and stdout+stderr synchronously. Danke für die Antworten!

Antwort

27

In bash, einem Prozess Substitution Substitution Befehl foo > >(bar) beendet, sobald foo Oberflächen zu beantworten. (Dies ist in der Dokumentation nicht diskutiert.) Sie können dies mit

: > >(sleep 1; echo a) 

Dieser Befehl kehrt sofort, dann druckt a asynchron eine Sekunde später.

In Ihrem Fall dauert der Befehl tee nur ein wenig Zeit, um zu beenden, nachdem command abgeschlossen ist. Hinzufügen sync gab tee genug Zeit zu vervollständigen, aber dies entfernt nicht die Race-Bedingung, mehr als das Hinzufügen einer sleep würde, macht es nur das Rennen eher unwahrscheinlich zu manifestieren.

Allgemeiner hat sync keinen intern beobachtbaren Effekt: es macht nur einen Unterschied, wenn Sie auf ein Gerät zugreifen möchten, auf dem Ihre Dateisysteme unter einer anderen Betriebssysteminstanz gespeichert sind. Wenn Ihr System Strom verliert, sind nur Daten, die vor dem letzten sync geschrieben wurden, garantiert verfügbar, nachdem Sie den Computer neu gestartet haben.

Wie für die Race-Bedingung zu entfernen, sind hier einige mögliche Ansätze:

  • Explizit alle substituierten Prozesse synchronisieren.

    mkfifo sync.pipe 
    command > >(tee -- "$stdoutF"; echo >sync.pipe) 
         2> >(tee -- "$stderrF"; echo >sync.pipe) 
    read line < sync.pipe; read line < sync.pipe 
    
  • einen anderen temporären Dateinamen stattdessen für jeden Befehl Verwenden von $stdoutF und $stderrF Wiederverwendung und durchzusetzen, dass die temporäre Datei neu erstellt wird immer.

  • Prozesssubstitution aufgeben und stattdessen Rohre verwenden.

    { { command | tee -- "$stdoutF" 1>&3; } 2>&1 \ 
          | tee -- "$stderrF" 1>&2; } 3>&1 
    

    Wenn Sie den Rückgabestatus Befehl benötigen, bash setzt es in ${PIPESTATUS[0]}.

    { { command | tee -- "$stdoutF" 1>&3; exit ${PIPESTATUS[0]}; } 2>&1 \ 
          | tee -- "$stderrF" 1>&2; } 3>&1 
    if [ ${PIPESTATUS[0]} -ne 0 ]; then echo command failed; fi 
    
+0

Dank für diese tolle Antwort, dies hat mich nur eine bessere Admin! –

-1

ein sleep 5 oder Dingsbums anstelle von sync Legen Sie Ihre letzte Frage

1

Ich habe manchmal eine Wache:

: > >(sleep 1; echo a; touch guard) \ 
    && while true; do 
    [ -f "guard" ] && { rm guard; break; } 
    sleep 0.2 
    done