Wie funktioniert Piping/Umleitung in Linux genau?

Wie funktioniert Piping/Umleitung in Linux genau?

Ich habe diese drei Beispiele für die Umleitung von stdin/stdout, aber nur eines davon funktioniert wie vorgesehen. Ich wäre froh, wenn mir das jemand erklären könnte.

Das Ziel besteht darin, den Inhalt in Datei1 zu sortieren und die Änderungen in derselben Datei zu speichern.

  1. sort file1 | tee file1 > /dev/null --------> Es funktioniert

  2. sort file1 | tee file1 --------> Inhalt von file1 wird gelöscht

  3. sort file1 | tee file1 > file2 --------> Inhalt von file1 wird gelöscht

PS. tee kopiert die Standardeingabe in jede DATEI und auch in die Standardausgabe.

Warum funktioniert das erste Beispiel?

Antwort1

Laut meinen Tests mit Debian Wheezy können alle drei Szenarien zu beiden Ergebnissen führen (Datei1 wird sortiert und in sich selbst zurückgeschrieben ODER nichts wird sortiert und nichts wird in Datei1 geschrieben).

Ich glaube, das ist ein normales Verhalten und kommt von der Art, wie Linux mit Dateien arbeitet. Denken Sie an den Befehl – ​​der Befehl sort beginnt, Datei1 zu lesen und sendet seine Ausgabe sofort an tee. tee liest die Ausgabe, schreibt sie zurück in Datei1 und druckt sie in /dev/null. Falls sort schnell genug ist, um die ganze Datei1 zu lesen, erhält tee eine sortierte Ausgabe. Aber falls tee die Datei sperrt, löscht es sie (tee löscht immer die Ausgabedatei, außer wenn die Anfügeoption verwendet wird) und das ist so ziemlich das, was in allen Ihren 3 Szenarien passiert.

Um es kürzer zu machen, sagen wir, dass sort manchmal nicht schnell genug ist, um file1 zu lesen. In einem solchen Fall löscht tee die Datei, BEVOR sort sie lesen kann.

Ich würde folgendes Vorgehen empfehlen:

cat file1 | sort > /tmp/sorting.tmp; mv /tmp/sorting.tmp file1

Falls Sie die sortierte Ausgabe auf stdout sehen möchten, gehen Sie wie folgt vor:

cat file1 | sort | tee /tmp/sorting.tmp; mv /tmp/sorting.tmp file1

Es ist keine gute Idee, auf Multiprozessorsystemen zwei verschiedene Befehle mit einer Datei arbeiten zu lassen – Sie können nie sicher sein, welcher zuerst ausgeführt wird. Auf einem Single-Thread-System wäre das Verhalten anders – sequentiell.

Antwort2

Ich bezweifle, dass dieses Verhalten vorhersehbar ist (und sicherlich nicht davon abhängen würde). Der teeBefehl startet wahrscheinlich einen neuen Prozess, um seine Eingabe an das „andere“ Ziel zu senden. Das Betriebssystem „puffert“ die Ausgabe, bis es den Punkt erreicht, an dem es die Zieldatei erstellt und seinen temporären Puffer in die Datei schreibt. Der genaue Zeitpunkt, zu dem dies geschieht (und die Quelle überschreibt), hängt wahrscheinlich von Folgendem ab:

  • Die Größe der Datei und der verfügbare Speicher für den Puffer
  • Die verstrichene Zeit
  • Wenn die Eingabe von der Pipe zu teebeendet ist

Das geht tiefer als bash: Es ist die Art und Weise, wie die Programme arbeiten, die bashanfängt. Die Shell interpretiert nur die Befehle, die Sie eingeben, und startet die Programme, die zur Ausführung der Befehle benötigt werden. Die Shell hat keine Kontrolle darüber, wie jedes Programm arbeitet, und noch weniger darüber, wie diese Programme interagieren. Ein Programm (oder eine Reihe von Programmen) aufzufordern, Daten aus einer Eingabedatei zu entnehmen und das Ergebnis im selben Satz in dieselbe Eingabedatei zu schreiben, liegt in der Verantwortung des Benutzers.

Vergessen Sie nicht, dass Bash nur der Interpreter von Benutzerbefehlen ist: Es handelt sich lediglich um eine shellMöglichkeit für das Betriebssystem, Benutzerabsichten in Systemaufrufe umzuwandeln.

Und seindokumentiert, auch! Oderdiese Mail, das ähnliche Probleme behandelt. Oder diesesStackOverflow-Thread. Oderdieser Serverfault-Thread.

Beachten Sie, dass dies auch bei der Umleitung von passieren kann stdin: Wenn Sie Eingabebefehle aus einer Datei übernehmen: $ myprog < commandfile. Beim myprogSchreiben in die Befehlsdatei gibt es keine Garantie, dass alle commandfileBefehle ausgeführt werden.

Eine wirklich einfache Analogie wäre etwa diese Anweisungsliste:

- Execute the instructions step by step
- Dip this instruction list in a bucket of black paint
- Type in the following commands:
  find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \
  | grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'

Ich nehme an, Sie würden zuerst eine Kopie erstellen? (Befehl übernommen vonErweitertes Bash-Scripting-Handbuch)

Antwort3

Sie möchten also, dass der ursprüngliche Inhalt der Datei erhalten bleibt, während die Änderungen an die Datei angehängt werden?

tee überschreibt standardmäßig Schreibvorgänge. Versuchen Sie, mit dem Flag -a die Änderungen an die Datei anzuhängen.

Antwort4

sort file1 | tee file1 > tmp && mv file1 original && mv tmp file1

Sie können die Datei in einen Platzhalter schreiben, das Original in eine Sicherungskopie umbenennen und den Platzhalter anschließend in das Original verschieben.

verwandte Informationen