Betrachten Sie dieses Skript:
tmpfile=$(mktemp)
cat <<EOS > "$tmpfile"
line 1
line 2
line 3
EOS
cat <(tail -1 "$tmpfile") "$tmpfile"
Dies funktioniert und gibt aus:
line 3
line 1
line 2
line 3
Nehmen wir an, unsere Eingabequelle war keine tatsächliche Datei, sondern stdin:
cat <<EOS | # what goes here now?
line 1
line 2
line 3
EOS
Wie ändern wir den Befehl:
cat <(tail -1 "$tmpfile") "$tmpfile"
Damit es in diesem unterschiedlichen Kontext immer noch die gleiche Ausgabe erzeugt?
HINWEIS: Der spezifische Heredoc, den ich catte, sowie die Verwendung eines Heredoc selbst dienen lediglich der Veranschaulichung. Jede akzeptable Antwort sollte davon ausgehen, dass sie beliebige Daten über stdin empfängt .
Antworten:
Versuchen:
Beispiel
Definieren Sie eine Variable mit unserer Eingabe:
Führen Sie unseren Befehl aus:
Alternativ könnten wir natürlich ein here-doc verwenden:
Wie es funktioniert
x=x $0 ORS
Dadurch wird jede Eingabezeile an die Variable angehängt
x
.In awk
ORS
ist das Trennzeichen für den Ausgabedatensatz . Standardmäßig ist es ein Zeilenumbruchzeichen.END{printf "%s", $0 ORS x}
Nachdem wir die gesamte Datei eingelesen haben, wird die letzte Zeile gedruckt
$0
, gefolgt vom Inhalt der gesamten Dateix
.Da dies die gesamte Eingabe in den Speicher liest, wäre es für große ( z . B. Gigabyte) Eingaben nicht geeignet .
quelle
tee
ist, aber von einem Standard und einer Datei würden wir den gleichen Standard in zwei verschiedene Prozessersetzungen leiten. oder irgendetwas, das dem ungefähr gleichwertig wäre?Wenn stdin auf eine durchsuchbare Datei verweist (wie im Fall von Bashs (aber nicht allen anderen Shell-Dokumenten), die mit temporären Dateien implementiert sind), können Sie den Schwanz abrufen und dann zurücksuchen, bevor Sie den vollständigen Inhalt lesen:
suchen Betreiber in den zur Verfügung stehen
zsh
oderksh93
Muscheln, oder Skriptsprachen wie Tcl / Perl / Python, aber nicht inbash
. Sie können diese fortgeschrittenen Dolmetscher jedoch jederzeit anrufen,bash
wenn Sie sie verwenden müssenbash
.Oder
Das funktioniert nicht, wenn stdin auf nicht durchsuchbare Dateien wie eine Pipe oder einen Socket verweist. Dann besteht die einzige Möglichkeit darin, die gesamte Eingabe zu lesen und zu speichern (im Speicher oder in einer temporären Datei ...).
Einige Lösungen zum Speichern im Speicher wurden bereits angegeben.
Mit einem Tempfile
zsh
könnten Sie es tun mit:Wenn Sie unter Linux mit
bash
oderzsh
einer Shell arbeiten, die temporäre Dateien für Here-Dokumente verwendet, können Sie die temporäre Datei, die von einem Here-Dokument erstellt wurde, tatsächlich zum Speichern der Ausgabe verwenden:quelle
Das Problem bei der Übersetzung in etwas, das verwendet
tail
wird, besteht darin, dasstail
die gesamte Datei gelesen werden muss, um das Ende zu finden. Um das in Ihrer Pipeline zu verwenden, müssen Sietail
.cat
.Das schwierige Bit besteht nicht darin, den Inhalt des Dokuments zu duplizieren (
tee
tut dies), sondern die Ausgabe vontail
zu erhalten, bevor der Rest des Dokuments ausgegeben wird, ohne eine temporäre Zwischendatei zu verwenden.Durch die Verwendung
sed
(oderawk
wie bei John1024 ) werden das doppelte Parsen der Daten und das Ordnungsproblem durch Speichern der Daten im Speicher beseitigt .Die
sed
Lösung, die ich vorschlage, ist zu1{h;d;}
Speichern Sie die erste Zeile unverändert im Haltebereich und fahren Sie mit der nächsten Zeile fort.H
Fügen Sie einander eine Zeile mit einer eingebetteten neuen Zeile an den Haltebereich an.${G;p;}
Fügen Sie den Haltebereich mit einer eingebetteten neuen Zeile an die letzte Zeile an und drucken Sie die resultierenden Daten.Dies ist eine wörtliche Übersetzung der Lösung von John1024 in
sed
, mit der Einschränkung, dass der POSIX-Standard nur garantiert, dass der Speicherplatz mindestens 8192 Bytes (8 KiB) beträgt. Es wird jedoch empfohlen , diesen Puffer nach Bedarf dynamisch zuzuweisen und zu erweitern, was beide GNUsed
und BSDsed
macht).Wenn Sie sich erlauben, eine Named Pipe zu verwenden:
Dies dient
tee
zum Senden der Daten nach untenmypipe
und gleichzeitig ancat
. Dascat
Dienstprogrammtail
liest zuerst die Ausgabe von (die von liest, vonmypipe
der geschriebentee
wird) und hängt dann die Kopie des Dokuments an, von der direkt stammttee
.Dies ist jedoch ein schwerwiegender Fehler: Wenn das Dokument zu groß ist (größer als die Puffergröße der Pipe), wird in das Dokument
tee
geschriebenmypipe
undcat
blockiert, während darauf gewartet wird, dass die (unbenannte) Pipe leer wird. Es würde nicht geleert werden, bis es darauscat
gelesen wird.cat
würde nicht davon lesen, bistail
es fertig war. Undtail
würde nicht fertig werden, bistee
fertig war. Dies ist eine klassische Deadlock-Situation.Die Variation
hat das gleiche Problem.
quelle
sed
funktioniert nicht, wenn der Eingang nur eine Zeile hat (vielleichtsed '1h;1!H;$!d;G'
). Beachten Sie auch, dass einigesed
Implementierungen eine niedrige Grenze für die Größe ihres Musters und des Speicherplatzes haben.Es gibt ein Tool, das
pee
in einer Sammlung von Befehlszeilendienstprogrammen mit dem Namen "moreutils" (oder auf andere Weise von der Homepage abrufbar ) benannt ist.Wenn Sie es auf Ihrem System haben können, lautet das Äquivalent für Ihr Beispiel wie folgt:
pee
Die Reihenfolge der durchlaufenden Befehle ist wichtig, da sie in der angegebenen Reihenfolge ausgeführt werden.quelle
Versuchen:
Da es sich bei dem Ganzen um Literaldaten handelt (ein "Hier-ist-Dokument") und der Unterschied zwischen dieser und der gewünschten Ausgabe trivial ist, massieren Sie diese Literaldaten genau dort, um sie an die Ausgabe anzupassen.
Nehmen wir nun an, es
line 3
kommt von irgendwoher und wird in einer Variablen namens gespeichertlastline
:In einem hier gezeigten Dokument können wir Text durch Ersetzen von Variablen generieren. Darüber hinaus können wir Text mithilfe der Befehlssubstitution berechnen:
Wir können mehrere Zeilen interpolieren:
Vermeiden Sie im Allgemeinen die Textverarbeitung der hier vorgelegten Dokumentvorlage. Versuchen Sie, es mit interpoliertem Code zu generieren.
quelle
cat <<EOS...
im OP war nur ein Beispiel für "Catting einer beliebigen Datei", um den Beitrag spezifisch und die Frage klar zu machen. War Ihnen das wirklich nicht klar, oder dachten Sie nur, es wäre klug, die Frage wörtlich zu interpretieren?Wenn Sie sich nicht für die Bestellung interessieren. Dann wird das funktionieren
cat lines | tee >(tail -1)
. Wie andere gesagt haben. Sie müssen die Datei zweimal lesen oder die gesamte Datei puffern, um dies in der von Ihnen gewünschten Reihenfolge zu tun.quelle