Zeige nur stderr auf dem Bildschirm, aber schreibe sowohl stdout als auch stderr in die Datei

24

Wie kann ich BASH-Magie verwenden, um dies zu erreichen?
Ich möchte nur die Ausgabe von stderr auf dem Bildschirm sehen,
aber ich möchte, dass sowohl stdout als auch stderr in eine Datei geschrieben werden.

Klarstellung: Ich möchte, dass sowohl stdout als auch stderr in derselben Datei landen. In der Reihenfolge passieren sie.
Leider tut dies keine der folgenden Antworten.

Andreas
quelle
2
Ein neuer Versuch mit
Gilles 'SO - hör auf böse zu sein'

Antworten:

14

Auch ohne Umleitung oder mit nichts anderem >logfile 2>&1kann nicht garantiert werden, dass die Ausgabe in der Reihenfolge der Generierung angezeigt wird.

Für den Anfang wird die Standardausgabe aus der Anwendung zeilenweise gepuffert (bis tty) oder gepuffert (bis zu einer Pipeline), aber stderr ist ungepuffert, sodass die Beziehungen zwischen der Reihenfolge der Ausgabe für einen Leser unterbrochen werden. Nachfolgende Phasen in einer Pipeline, die Sie erstellen könnten, erhalten keinen deterministisch geordneten Zugriff auf die beiden Streams (dies sind konzeptionell parallele Vorgänge, und Sie sind immer dem Scheduler unterworfen) geschrieben in beide Pipes, man kann nicht sagen, was zuerst kam).

"Die Reihenfolge, in der sie eintreten" ist nur der Anwendung wirklich bekannt. Die Reihenfolge der Ausgabe über stdout / stderr ist ein bekanntes - vielleicht klassisches - Problem.

jmb
quelle
7

Wenn Sie die Konstruktion verwenden: 1>stdout.log 2>&1Sowohl stderr als auch stdout werden in die Datei umgeleitet, da die stdout- Umleitung vor der stderr- Umleitung eingerichtet wird.

Wenn Sie die Reihenfolge umkehren, können Sie stdout in eine Datei umleiten und dann stderr in stdout kopieren , damit Sie es weiterleiten können tee.

$ cat test
#!/bin/sh
echo OUT! >&1
echo ERR! >&2

$ ./test 2>&1 1>stdout.log | tee stderr.log
ERR!

$ cat stdout.log
OUT!

$ cat stderr.log
ERR!
mmoya
quelle
Damit können Sie die beiden Streams nicht in derselben Datei protokollieren.
artistoex
1
@artistoex: Sie können nur tee -adieselbe Datei verwenden, die in verwendet wird, 1>um beide Ausgaben in derselben Datei zu summieren. So etwas wie:./test 2>&1 1>out+err.log | tee -a out+err.log
mmoya
Ich habe keine Ahnung warum, aber das funktioniert bei wget -O - www.google.demir nicht. (vergleiche mit der Lösung unten)
artistoex
4
Die Verwendung von tee -awird nicht zuverlässig funktionieren, um dieselbe Ausgabe zu erzielen, die sich auf der Konsole befinden würde, wenn nichts umgeleitet würde. Die übermittelte Ausgabe ist teemöglicherweise etwas verzögert im Vergleich zur Ausgabe, die direkt vom Befehl stammt, und wird daher möglicherweise später im Protokoll angezeigt.
Gilles 'SO - hör auf böse zu sein'
Das funktioniert bei mir nicht, out + err.log ist leer. Und ja, ich möchte stderror und standard in der gleichen Datei
Andreas
7

Ich konnte dies erreichen, indem ich die folgende Zeile oben in ein Bash-Skript einfügte:

exec 1>>log 2> >(tee -a log >&2)

Dadurch wird stdout in die Datei log( 1>>log) umgeleitet , und stderr wird in die Datei log( 2> >(tee -a log) umgeleitet >&2). Auf diese Weise erhalte ich eine einzelne Datei log, die sowohl stdout als auch stderr in der angegebenen Reihenfolge anzeigt, und stderr auch wie gewohnt auf dem Bildschirm angezeigt.

Der Haken ist, dass es nur zu funktionieren scheint, wenn ich an die Datei anhänge. Wenn ich nichts anhänge, scheinen sich die beiden Umleitungen zu überlasten und ich erhalte nur die zuletzt ausgegebenen.

bjg222
quelle
Gibt es eine Möglichkeit, mit dieser Option die STDERR-Zeilen so zu "ändern", dass sie eine benutzerdefinierte Zeichenfolge enthalten, sodass Sie problemlos darauf zugreifen können?
IMTheNachoMan
1

Sie möchten den Fehlerstrom duplizieren, damit er sowohl in der Konsole als auch in der Protokolldatei angezeigt wird. Das Tool dafür ist teeund alles, was Sie tun müssen, ist es auf den Fehlerstrom anzuwenden. Leider gibt es kein Standard-Shell-Konstrukt, um den Fehlerstrom eines Befehls in einen anderen Befehl umzuleiten, sodass eine kleine Neuanordnung der Dateideskriptoren erforderlich ist.

{ { echo out; echo err 1>&2; } 2>&1 >&3 | tee /dev/tty; } >log 3>&1
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^   ^^^^^^^^^^^^    ^^^^^^^^^
  command produces output      stdout3                    log
  command produces error       stderr1   dup to terminal  log
Gilles 'SO - hör auf böse zu sein'
quelle
Das funktioniert auch bei mir nicht ganz, ich bekomme zuerst stdout-Zeilen und dann alle stderr-Zeilen in der 'log'-Datei. Beispiel: {{echo out; echo err 1> & 2; echo out2; echo err2 1> & 2; } 2> & 1> & 3 | tee / dev / tty; }> log 3> & 1
Andreas
1
@ Andreas: Oh, duh, teeauch hier kommt eine Verzögerung. Ich glaube nicht, dass es eine reine Shell-Lösung gibt. Tatsächlich frage ich mich, ob es eine Lösung gibt, ohne die Anwendung in irgendeiner Weise zu verändern.
Gilles 'SO- hör auf böse zu sein'
1

So schreiben Sie stderr auf den Bildschirm und schreiben BOTH stderr und stdout in eine Datei - UND lassen die Zeilen für stderr und stdout in derselben Reihenfolge erscheinen, in der sie geschrieben würden, wenn beide auf den Bildschirm geschrieben würden:

Es stellt sich als schwieriges Problem heraus, insbesondere der Teil über "dieselbe Sequenz", den Sie erwarten würden, wenn Sie sie einfach auf den Bildschirm schreiben würden. Einfach ausgedrückt: Schreiben Sie jede Zeile in eine eigene Datei, markieren Sie jede Zeile (in jeder Datei) mit der genauen Uhrzeit, zu der die Zeile erstellt wurde, und führen Sie dann Folgendes aus: , aber um BEIDE "stderr" und "stdout" nacheinander zu sehen, sortieren Sie die beiden Dateien (mit exakten Zeitmarkierungen in jeder Zeile) zusammen.

Code:

# Set the location of output and the "first name" of the log file(s)
pth=$HOME
ffn=my_log_filename_with_no_extension
date >>$pth/$ffn.out
date >>$pth/$ffn.err
# Start background processes to handle 2 files, by rewriting each one line-by-line as each line is added, putting a label at front of line
tail -f $pth/$ffn.out | perl -nle 'use Time::HiRes qw(time);print substr(time."0000",0,16)."|1|".$_' >>$pth/$ffn.out.txt &
tail -f $pth/$ffn.err | perl -nle 'use Time::HiRes qw(time);print substr(time."0000",0,16)."|2|".$_' >>$pth/$ffn.err.txt &
sleep 1
# Remember the process id of each of 2 background processes
export idout=`ps -ef | grep "tail -f $pth/$ffn.out" | grep -v 'grep' | perl -pe 's/\s+/\t/g' | cut -f2`
export iderr=`ps -ef | grep "tail -f $pth/$ffn.err" | grep -v 'grep' | perl -pe 's/\s+/\t/g' | cut -f2`
# Run the command, sending stdout to one file, and stderr to a 2nd file
bash mycommand.sh 1>>$pth/$ffn.out 2>>$pth/$ffn.err
# Remember the exit code of the command
myexit=$?
# Kill the two background processes
ps -ef | perl -lne 'print if m/^\S+\s+$ENV{"idout"}/'
echo kill $idout
kill $idout
ps -ef | perl -lne 'print if m/^\S+\s+$ENV{"iderr"}/'
echo kill $iderr
kill $iderr
date
echo "Exit code: $myexit for '$listname', list item# '$ix', bookcode '$bookcode'"

Ja, dies scheint aufwendig zu sein und führt zu 4 Ausgabedateien (von denen Sie 2 löschen können). Es scheint, dass dies ein schwer zu lösendes Problem ist, daher waren mehrere Mechanismen erforderlich.

Um die Ergebnisse von BOTH stdout und stderr in der erwarteten Reihenfolge zu sehen, führen Sie Folgendes aus:

cat $pth/$ffn.out.txt $pth/$ffn.err.txt | sort

Der einzige Grund, warum die Sequenz zumindest sehr nahe an dem liegt, was wäre, wenn sowohl stdout als auch stderr einfach auf dem Bildschirm angezeigt worden wären, ist: Jede einzelne Zeile ist bis zur Millisekunde mit einem Zeitstempel versehen.

Gehen Sie wie folgt vor, um stderr im Verlauf des Vorgangs auf dem Bildschirm anzuzeigen:

tail -f $pth/$ffn.out

Hoffe das hilft jemandem, der hier lange angekommen ist, nachdem die ursprüngliche Frage gestellt wurde.

Dana Forsberg
quelle
+1 für die bloße Anstrengung, ich hatte eine ähnliche Idee (Zeitstempel beider Streams über Perl), aber ich hatte Mühe, es zu implementieren. Ich werde untersuchen, ob es für mich funktioniert (dh, ob es wirklich die Reihenfolge der Ausgaben einhält, da andere Lösungen hier die Dinge aufgrund der Asynchronität zwischen dem Markieren von stderr und stdout etwas verschieben)
Olivier Dulac
0

Geben Sie fden Befehl ein, den Sie ausführen möchten

( exec 3>/tmp/log; f 2>&1 1>&3 |tee >(cat)>&3 )

sollte dir geben, was du wünschst. Zum Beispiel wget -O - www.google.dewürde so aussehen:

( exec 3>/tmp/googlelog; wget -O - www.google.de 2>&1 1>&3 |tee >(cat)>&3 )
artistoex
quelle
1
Das funktioniert fast für mich, aber in der Protokolldatei bekomme ich zuerst alle stdout-Zeilen und dann alle stderror-Zeilen. Ich möchte, dass sie in der natürlichen Reihenfolge verschachtelt werden, in der sie geschehen.
Andreas
0

Angesichts dessen, was ich hier gelesen habe, besteht die einzige Lösung darin, jeder Zeile, ob STOUT oder STERR, eine Zeitscheibe / einen Zeitstempel voranzustellen. Datum +% s würde zu Sekunden werden, aber das ist zu langsam, um die Ordnung aufrechtzuerhalten. Auch das Protokoll müsste irgendwie sortiert werden. Mehr Arbeit als ich kann.

müder alter Typ
quelle
0

Ich habe mit genau dieser Anforderung zu kämpfen und konnte am Ende keine einfache Lösung finden. Was ich stattdessen getan habe, war Folgendes:

TMPFILE=/tmp/$$.log
myCommand > $TMPFILE 2>&1
[ $? != 0 ] && cat $TMPFILE
date >> myCommand.log
cat $TMPFILE >> myCommand.log
rm -f $TMPFILE

Es hängt stdout und stderr in der richtigen Reihenfolge an die Protokolldatei an. Und wenn Fehler aufgetreten sind (wie durch den Beendigungsstatus des Befehls bestimmt), sendet er die gesamte Ausgabe (stdout und stderr) wieder in der richtigen verschachtelten Reihenfolge an stdout. Ich fand das ein vernünftiger Kompromiss für meine Bedürfnisse. Wenn Sie eine Protokolldatei mit nur einem Durchlauf anstelle einer wachsenden Protokolldatei mit mehreren Durchläufen möchten, ist dies noch einfacher:

myCommand > myCommand.log 2>&1
[ $? != 0 ] && cat myCommand.log
Keith Pomakis
quelle
0

stderr bis befehlsersetzt tee -aund stdout an dieselbe Datei angehängt:

./script.sh 2> >(tee -a outputfile) >>outputfile

und beachte: achte auch auf die richtige Reihenfolge (aber ohne stderr-show), es gibt einen 'Unbuffer'-Befehl von' expect'-Tools, der tty simuliert und stdout / err in der Reihenfolge hält, wie es auf dem Terminal angezeigt würde:

unbuffer ./script.sh > outputfile
Asain Kujovic
quelle