Erzwingen Sie, dass das Bash-Skript tee verwendet, ohne über die Befehlszeile weiterzuleiten

20

Ich habe ein Bash-Skript mit viel Ausgabe und möchte dieses Skript ändern (kein neues erstellen), um die gesamte Ausgabe in eine Datei zu kopieren, so wie es passieren würde, wenn ich sie durch tee leiten würde.

Ich habe eine Datei script.sh :

#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini

und ich möchte eine Kopie von STDOUT in einer Datei script.log erstellen, ohne ./script.sh | tee script.logjedes Mal eingeben zu müssen .

Danke, Tom

Tom
quelle
Es tut mir leid, dass ich das nicht früher gesagt habe, aber das Skript ist ziemlich lang, weshalb ich nach einer einfachen Lösung suche. Anhängen | Abschlag zu jeder Zeile ist etwas zu viel.
Tom

Antworten:

15

Ich konnte Dennis 'sehr einfachen Einzeiler nicht zum Laufen bringen, daher hier eine weitaus kompliziertere Methode. Ich würde sein erstes versuchen.

Wie bereits erwähnt, können Sie exec verwenden, um Standardfehler und Standardausgabe für das gesamte Skript umzuleiten. So:
exec > $LOGFILE 2>&1 Dies gibt alle stderr und stdout in $ LOGFILE aus.

Da dies nun in der Konsole sowie in einer Protokolldatei angezeigt werden soll, müssen Sie auch eine Named Pipe verwenden, in die Exec schreiben und von der Tee lesen kann.
(Dennis 'Einzeiler macht dies auch technisch, wenn auch offensichtlich auf andere Weise). Die Pfeife selbst wird mit erzeugt mkfifo $PIPEFILE. Dann machen Sie folgendes.

# Fange an, ein T-Stück in eine Logdatei zu schreiben, aber ziehe seine Eingabe von unserer Named Pipe.
tee $ LOGFILE <$ PIPEFILE &

# Prozess-ID des Abschlags für den Wartebefehl erfassen.
TEEPID = $!

# Leite den Rest von stderr und stdout zu unserer Named Pipe um.
exec> $ PIPEFILE 2> & 1

Echo "Machen Sie Ihre Befehle hier"
echo "Alle ihre Standard-Outs werden erledigt."
echo "So wird ihr Standardfehler"> & 2

# Schließen Sie die Dateideskriptoren stderr und stdout.
exec 1> & - 2> & -

# Warten Sie, bis der Tee fertig ist, da jetzt das andere Ende der Leitung geschlossen ist.
warte $ TEEPID

Wenn Sie gründlich sein möchten, können Sie die Named-Pipe-Datei am Anfang und Ende Ihres Skripts erstellen und löschen.

Fürs Protokoll habe ich das meiste davon aus dem sehr informativen Blog-Post eines zufälligen Mannes entnommen: ( Archivierte Version )

Christopher Karel
quelle
Dies scheint in Cygwin nicht zu funktionieren. :-(
docwhat
Dieser Beitrag leistet einen guten Job in der Ausbildung. Vielen Dank.
Felipe Alvarez
27

Fügen Sie dies einfach am Anfang Ihres Skripts ein:

exec > >(tee -ia script.log)

Dadurch werden alle an stdout gesendeten Ausgaben an die Datei angehängt script.logund der vorherige Inhalt bleibt erhalten . Wenn Sie jedes Mal neu starten möchten, wenn das Skript ausgeführt wird, fügen Sie es rm script.logdirekt vor diesem execBefehl hinzu oder entfernen Sie das Symbol -aaus dem teeBefehl.

Die -iOption bewirkt tee, dass Interruptsignale ignoriert werden und möglicherweise teeein vollständigerer Ausgabesatz abgefangen wird.

Fügen Sie diese Zeile hinzu, um auch alle Fehler abzufangen (in einer separaten Datei):

exec 2> >(tee -ia scripterr.out)

Die Leerzeichen zwischen den mehreren >Symbolen sind wichtig.

Bis auf weiteres angehalten.
quelle
Sehr interessante Lösung.
ℝaphink
2
Dies ist eine wirklich elegante Art, es zu tun, verglichen mit all dem Mist, den ich gepostet habe. Wenn ich jedoch ein Skript mit diesen beiden Zeilen erstelle, bleibt es hängen und wird nie ordnungsgemäß beendet.
Christopher Karel
Welche Version von Bash?
Bis auf weiteres angehalten.
GNU bash, Version 3.2.25 (1) als Teil einer RHEL5.3-Installation. Es scheint, dass dies die neueste Version aus den offiziellen Repositories ist.
Christopher Karel
Ich habe es gerade unter Cygwin in Bash 3.2.49 (23) -Release ausprobiert und habe Dateideskriptorfehler bekommen. Ich arbeite in Bash 4.0.33 (1) -Release in Ubuntu 9.10, es kann also eine Bash 4-Sache sein.
Bis auf weiteres angehalten.
6

Dies ist eine kombinierte Version der Antwort, die Dennis Williamson zuvor gepostet hat. Fügt err & std in der richtigen Reihenfolge an script.log an.

Fügen Sie diese Zeile an den Anfang Ihres Skripts ein:

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

Beachten Sie den Abstand zwischen den >Zeichen. Funktioniert für mich mit GNU bash, Version 3.2.25 (1) -release (x86_64-redhat-linux-gnu)

ttt
quelle
5

Ich nehme an, ein anderer Ansatz ist ungefähr so:

#!/bin/bash

# start grouping at the top
{ 
    # your script goes here
    do_something_that_outputs_stuff_to_STDOUT
    launch_applications_that_output_to_STDOUT

# and at the bottom, redirect everything from the grouped commands
} 2>&1 | tee script.log 
Glenn Jackman
quelle
3

Wenn Sie eine Kopie der Ausgabe wünschen, können Sie teedies folgendermaßen tun:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT | tee script.log
  launch_applications_that_output_to_STDOUT | tee -a script.log
  fini

Dies wird jedoch nur stdout in script.log protokollieren. Wenn Sie sicherstellen möchten, dass sowohl stderr als auch stdout umgeleitet werden, verwenden Sie:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT 2>&1 | tee script.log
  launch_applications_that_output_to_STDOUT 2>&1 | tee -a script.log
  fini

Du könntest es sogar ein bisschen schöner machen mit einer kleinen Funktion:

  #!/bin/bash

  LOGFILE="script.log"
  # Wipe LOGFILE if you don't want to add to it at each run
  rm -f "$LOGFILE"

  function logcmd() {
        local cmd="$1" logfile=${LOGFILE:-script.log} # Use default value if unset earlier

        # Launch command, redirect stderr to stdout and append to $logfile
        eval '$cmd' 2>&1 | tee -a "$logfile"
  }

  init
  logcmd "do_something_that_outputs_stuff_to_STDOUT"
  logcmd "launch_applications_that_output_to_STDOUT"
  fini
ℝaphink
quelle
Gute Antwort. Wenn er sich wirklich nur um stdout kümmert, würde ich das einfach weglassen 2>&1und es zum Tee leiten.
DaveParillo
Das stimmt, ich habe nur stderr eingeschlossen, weil die vorherige Antwort von Igor darauf abzielte, sie umzuleiten, und ich fand, dass es eine gute Idee war.
ℝaphink
1

Das würdest du einfach benutzen:

script logfile.txt
your script or command here

Dies gibt alles in dieses Protokoll aus, alles und zeigt es auf dem Bildschirm an. Wenn Sie die Ausgabe FULL wünschen, gehen Sie wie folgt vor.

Und wenn Sie faul sind, können Sie das Skript erneut abspielen.

Phishy
quelle
Gut zu wissen, dass scriptes sich um ein Paket handelt, z. sudo apt-get install scriptum es auf Distributionen mit Debian- Geschmack zu installieren, script -ac 'command opts' ~/Documents/some_log.scriptkann es zusätzlich in einem Terminal über so etwas wie überprüft werden strings ~/Documents/some_log.script | more; stringsDies ist eine einfache Möglichkeit, einige der script injizierten Elemente vorab zu bereinigen , um ausgefeiltere Methoden für die Wiedergabe / Anzeige zu ermöglichen . Ansonsten eine solide Antwort @Phishy, ​​denn scriptauch interaktive Dinge bleiben erhalten.
S0AndS0
0
#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT > script.log 2>&1
launch_applications_that_output_to_STDOUT >> script.log 2>&1
fini

Weitere Informationen zur Funktionsweise finden Sie hier: http://www.xaprb.com/blog/2006/06/06/what-does-devnull-21-mean/

Igor Zinov'yev
quelle
1
Ich habe in Igors Antwort eine zweite> in die zweite Zeile eingefügt, damit das in der ersten Zeile geschriebene Protokoll nicht gelöscht wird.
Doug Harris
1
Er möchte jedoch eine Kopie, also muss er sie verwenden tee.
ℝaphink
@ Raphink: Das stimmt :)
Tom