So leiten Sie die Ausgabe in eine Datei um und stdout

989

In Bash anrufen foo würde ein jede Ausgabe dieses Befehls auf dem stdout anzeigen.

Berufung foo > output würde jede Ausgabe dieses Befehls in die angegebene Datei umleiten (in diesem Fall 'Ausgabe').

Gibt es eine Möglichkeit, die Ausgabe in eine Datei umzuleiten und auf stdout anzuzeigen?

SCdF
quelle
3
Wenn gerade jemand hier gelandet ist
Murphy
2
Ein Hinweis zur Terminologie: Wenn Sie ausführen foo > outputdie Daten werden geschrieben stdout und stdout ist die Datei mit dem Namen output. Das heißt, beim Schreiben in die Datei wird in stdout geschrieben. Sie fragen, ob es möglich ist, sowohl in stdout als auch in das Terminal zu schreiben.
William Pursell
2
@WilliamPursell Ich bin nicht sicher, ob Ihre Klarstellung die Dinge verbessert :-) Wie wäre es damit: OP fragt, ob es möglich ist, das stdout des aufgerufenen Programms sowohl auf eine Datei als auch auf das stdout des aufrufenden Programms zu leiten (letzteres ist das stdout, das das aufgerufene Programm würde erben, wenn nichts Besonderes getan wurde (dh das Terminal, wenn das aufrufende Programm eine interaktive Bash-Sitzung ist). Und vielleicht möchten sie auch das stderr des aufgerufenen Programms auf ähnliche Weise steuern ("jede Ausgabe von diesem Befehl" könnte vernünftigerweise so interpretiert werden, dass es stderr einschließt).
Don Hatch

Antworten:

1379

Der gewünschte Befehl heißt tee:

foo | tee output.file

Zum Beispiel, wenn Sie sich nur für stdout interessieren:

ls -a | tee output.file

Wenn Sie stderr einschließen möchten, gehen Sie wie folgt vor:

program [arguments...] 2>&1 | tee outfile

2>&1Leitet Kanal 2 (stderr / Standardfehler) in Kanal 1 (stdout / Standardausgang) um, sodass beide als stdout geschrieben werden. Es wird ab dem teeBefehl auch an die angegebene Ausgabedatei weitergeleitet .

Wenn Sie an die Protokolldatei anhängen möchten, verwenden Sie außerdem Folgendestee -a :

program [arguments...] 2>&1 | tee -a outfile
Zoredache
quelle
173
Wenn OP möchte, dass "alle Ausgaben" umgeleitet werden, müssen Sie auch stderr greifen: "ls -lR / 2> & 1 | tee output.file"
James Brady
45
@evanmcdonnal Die Antwort ist nicht falsch, sie ist möglicherweise nicht spezifisch genug oder vollständig, abhängig von Ihren Anforderungen. Es gibt sicherlich Bedingungen, unter denen Sie möglicherweise nicht möchten, dass stderr als Teil der Ausgabe in einer Datei gespeichert wird. Als ich dies vor 5 Jahren beantwortete, ging ich davon aus, dass das OP nur stdout wollte, da er stdout im Betreff des Beitrags erwähnte.
Zoredache
3
Tut mir leid, ich könnte ein wenig verwirrt gewesen sein. Als ich es versuchte, bekam ich einfach keine Ausgabe, vielleicht würde alles auf stderr gehen.
evanmcdonnal
38
Verwenden Sie -aargument on tee, um Inhalte anzuhängen output.file, anstatt sie zu überschreiben:ls -lR / | tee -a output.file
kazy
16
Wenn Sie $?danach verwenden, wird der Statuscode von zurückgegeben tee, der wahrscheinlich nicht Ihren Wünschen entspricht. Stattdessen können Sie verwenden ${PIPESTATUS[0]}.
LaGoutte
501
$ program [arguments...] 2>&1 | tee outfile

2>&1 Dumps der Streams stderr und stdout. tee outfilenimmt den Stream, den es bekommt und schreibt ihn auf den Bildschirm und in die Datei "outfile".

Dies ist wahrscheinlich das, wonach die meisten Menschen suchen. Die wahrscheinliche Situation ist, dass ein Programm oder Skript lange Zeit hart arbeitet und viel Ausgabe produziert. Der Benutzer möchte es regelmäßig auf Fortschritt überprüfen, möchte aber auch, dass die Ausgabe in eine Datei geschrieben wird.

Das Problem (insbesondere beim Mischen von stdout- und stderr-Streams) besteht darin, dass die Streams vom Programm geleert werden. Wenn zum Beispiel alle Schreibvorgänge auf stdout sind nicht gespült, aber alle die Schreibvorgänge auf stderr sind , werden sie in der Ausgabedatei und auf dem Bildschirm in chronologischer Reihenfolge angezeigt.

Es ist auch schlecht, wenn das Programm nur alle paar Minuten 1 oder 2 Zeilen ausgibt, um den Fortschritt zu melden. In einem solchen Fall würde der Benutzer, wenn die Ausgabe nicht vom Programm geleert würde, stundenlang keine Ausgabe auf dem Bildschirm sehen, da nichts davon stundenlang durch das Rohr geschoben würde.

Update: Das Programm unbuffer, Teil des expectPakets, löst das Pufferproblem. Dies führt dazu, dass stdout und stderr sofort auf den Bildschirm und die Datei schreiben und diese synchron halten, wenn sie kombiniert und umgeleitet werden tee. Z.B:

$ unbuffer program [arguments...] 2>&1 | tee outfile
Matthew Alpert
quelle
7
Kennt jemand einen "Unbuffer" für osx?
John Mee
7
Wenn Sie Unbuffer nicht verwenden können, enthält GNU Coreutils ein Tool namens stdbuff , mit dem Sie die Ausgabepufferung wie folgt verhindern können$ stdbuf -o 0 program [arguments...] 2>&1 | tee outfile
Peter
1
Wenn Sie OS X verwenden, ist es in den meisten Fällen geringfügig einfacher, den Puffer zu verwenden, jedoch nicht, wenn Sie Signale an den von Ihnen ausgeführten Befehl übergeben möchten. Mein Kommentar richtete sich eher an Benutzer unter Linux, bei denen coreutils höchstwahrscheinlich standardmäßig installiert ist. @Ethan, sehen Sie diese Antwort, wenn Sie über stdbuf Syntax verwirrt sind: stackoverflow.com/a/25548995/942781
Peter
3
Beachten Sie, dass pythoneine Option integriert ist -u, die die Ausgabe entpuffert.
fabian789
1
Absoluter Lebensretter für das Training von ML-Modellen, bei denen ich ohne zusätzlichen Code protokollieren möchte, aber auch die Ausgabe über die Stunden / Tage sehen möchte, die für die Ausführung erforderlich sind. Vielen Dank!
Rokoko
126

Ein anderer Weg, der für mich funktioniert, ist:

<command> |& tee  <outputFile>

wie im gnu bash Handbuch gezeigt

Beispiel:

ls |& tee files.txt

Wenn '| &' verwendet wird, wird der Standardfehler von Befehl1 zusätzlich zu seiner Standardausgabe über die Pipe mit der Standardeingabe von Befehl2 verbunden. es ist eine Abkürzung für 2> & 1 |. Diese implizite Umleitung des Standardfehlers zur Standardausgabe erfolgt nach allen durch den Befehl angegebenen Umleitungen.

Weitere Informationen finden Sie unter Umleitung

Tsenapathie
quelle
10
Wenn Sie $?danach verwenden, wird der Statuscode von zurückgegeben tee, der wahrscheinlich nicht Ihren Wünschen entspricht. Stattdessen können Sie verwenden ${PIPESTATUS[0]}.
LaGoutte
1
Diese Methode verwirft farbige Konsolenausgaben. Irgendwelche Ideen?
Shakram02
Versuchen Sie: ls -l --color = auto | entpuffern tee files.txt
Luciano Andress Martini
17

Sie können hauptsächlich die Zoredache- Lösung verwenden . Wenn Sie die Ausgabedatei jedoch nicht überschreiben möchten, sollten Sie das Tee mit der Option -a wie folgt schreiben:

ls -lR / | tee -a output.file
O.Badr
quelle
11

Etwas hinzuzufügen ...

Der Paket-Entpuffer hat Support-Probleme mit einigen Paketen unter Fedora- und Redhat-Unix-Versionen.

Die Probleme beiseite legen

Das Folgende hat bei mir funktioniert

bash myscript.sh 2>&1 | tee output.log

Vielen Dank, ScDF & Matthew. Ihre Eingaben haben mir viel Zeit gespart.

nitinr708
quelle
7

Verwenden tail -f outputsollte funktionieren.

Chuck Phillips
quelle
3

Bonusantwort seit diesem Anwendungsfall hat mich hierher gebracht:

In dem Fall, in dem Sie dies als ein anderer Benutzer tun müssen

echo "some output" | sudo -u some_user tee /some/path/some_file

Beachten Sie, dass das Echo während Sie und das Schreiben der Datei als "some_user" ausgeführt werden. Dies funktioniert NICHT , wenn Sie das Echo als "some_user" ausführen und die Ausgabe mit >> "some_file" umleiten, da die Dateiumleitung erfolgt wie du.

Hinweis: tee unterstützt auch das Anhängen mit dem Flag -a. Wenn Sie eine Zeile in einer Datei als anderen Benutzer ersetzen müssen, können Sie sed als gewünschten Benutzer ausführen.

Jorfus
quelle
2

< command > |& tee filename # Dadurch wird eine Datei "Dateiname" mit dem Befehlsstatus als Inhalt erstellt. Wenn eine Datei bereits vorhanden ist, wird vorhandener Inhalt entfernt und der Befehlsstatus geschrieben.

< command > | tee >> filename # Dadurch wird der Status an die Datei angehängt, der Befehlsstatus wird jedoch nicht auf standard_output (Bildschirm) gedruckt.

Ich möchte etwas drucken, indem ich "Echo" auf dem Bildschirm verwende und diese Echo-Daten an eine Datei anhänge

echo "hi there, Have to print this on screen and append to a file" 
kiran sai
quelle
1

Sie können dies für Ihr gesamtes Skript tun, indem Sie am Anfang Ihres Skripts Folgendes verwenden:

#!/usr/bin/env bash

test x$1 = x$'\x00' && shift || { set -o pipefail ; ( exec 2>&1 ; $0 $'\x00' "$@" ) | tee mylogfile ; exit $? ; }

# do whaetever you want

Diese Umleitung beide stderr und stdout gibt an die Datei mit dem Namen mylogfile und alles lassen geht an stdout zugleich .

Es werden einige dumme Tricks verwendet:

  • Verwenden Sie execohne Befehl, um Umleitungen einzurichten.
  • verwenden tee, um Ausgaben zu duplizieren,
  • Starten Sie das Skript mit den gewünschten Weiterleitungen neu.
  • Verwenden Sie einen speziellen ersten Parameter (ein einfaches NULZeichen, das durch die $'string'spezielle Bash-Notation angegeben wird), um anzugeben, dass das Skript neu gestartet wird (von Ihrer ursprünglichen Arbeit darf kein gleichwertiger Parameter verwendet werden).
  • Versuchen Sie, den ursprünglichen Beendigungsstatus beizubehalten, wenn Sie das Skript mit der pipefailOption neu starten .

Hässlich aber nützlich für mich in bestimmten Situationen.

Bruno BEAUFILS
quelle
-13

Tee ist perfekt dafür, aber das wird auch den Job machen

ls -lr / > output | cat output
kal
quelle
10
Dies ist ein Fehler, wenn die Ausgabe noch nicht vorhanden ist und nicht das tut, was Sie möchten, wenn dies der Fall ist. Insgesamt ist sie unsinnig. Vielleicht meintest du ";" anstelle von "|" ?
Robert Gamble
6
Selbst wenn Sie a verwenden ;, wird die Ausgabe während eines langsamen Befehls stark verzögert.
Brad Koch