2008-11-24 8 views
119

Ist es möglich, die gesamte Ausgabe eines Bourne-Shell-Skripts an einen beliebigen Ort umzuleiten, aber mit Shell-Befehlen innerhalb des Skripts selbst?Wie leite ich die Ausgabe eines ganzen Shell-Skripts innerhalb des Skripts selbst um?

die Ausgabe eines einzigen Befehl Umleiten ist einfach, aber ich möchte etwas mehr wie folgt aus:

#!/bin/sh 
if [ ! -t 0 ]; then 
    # redirect all of my output to a file here 
fi 

# rest of script... 

Bedeutung: wenn das Skript nicht interaktiv ausgeführt wird (zum Beispiel cron), speichern Sie die Ausgabe aus von allem zu einer Datei. Wenn interaktiv von einer Shell aus ausgeführt wird, lasse die Ausgabe wie üblich auf stdout gehen.

Ich möchte dies für ein Skript tun, das normalerweise vom periodischen Dienstprogramm von FreeBSD ausgeführt wird. Es ist Teil des täglichen Laufs, den ich normalerweise nicht jeden Tag in E-Mails sehen möchte, also habe ich es nicht gesendet. Wenn jedoch etwas in diesem einen bestimmten Skript fehlschlägt, ist das wichtig für mich, und ich möchte in der Lage sein, die Ausgabe dieses einen Teils der täglichen Jobs zu erfassen und per E-Mail zu versenden.

Update: Joshua Antwort ist genau richtig, aber ich wollte auch stdout und stderr um das gesamte Skript speichern und wiederherzustellen, die wie dies geschehen ist:

# save stdout and stderr to file descriptors 3 and 4, then redirect them to "foo" 
exec 3>&1 4>&2 >foo 2>&1 

# ... 

# restore stdout and stderr 
exec 1>&3 2>&4 
+2

Testen für $ TERM ist nicht der beste Weg für den interaktiven Modus zu testen. Testen Sie stattdessen, ob stdin ein tty ist (test -t 0). –

+2

Mit anderen Worten: wenn [! -t 0]; dann exec> somefile 2> & 1; fi –

+0

Sehen Sie hier für all die Güte: [http://tldp.org/LDP/abs/html/io-redirection.html](http://tldp.org/LDP/abs/html/io-redirection.html) Was Joshua gesagt hat. exec> Datei leitet stdout in eine bestimmte Datei um, exec Loki

Antwort

107

Senden in einer Datei stdout

exec > file 

mit stderr

exec > file                  
exec 2>&1 

sowohl append stdout und stderr Datei

exec >> file 
exec 2>&1 
+7

Ich sage auch hinzufügen 2> & 1 an das Ende das, nur so stderr wird auch erwischt. :-) –

+1

Aufgeklärtes Abzeichen für die schnellste Waffe im Westen vor Jahren. – Joshua

+0

Sie haben keine Ahnung, wie viele erleuchtete Abzeichen ich auf diese Weise bekommen habe. :-P –

20

Sie können das gesamte Skript eine Funktion wie folgt machen:

main_function() { 
    do_things_here 
} 

dann am Ende des Skripts haben diese:

if [ -z $TERM ]; then 
    # if not run via terminal, log everything into a log file 
    main_function 2>&1 >> /var/log/my_uber_script.log 
else 
    # run via terminal, only output to screen 
    main_function 
fi 

Alternativ Sie alles in Logfile einloggen kann jeder Lauf und immer noch ausgeben es auf stdout durch einfaches tun:

# log everything, but also output to stdout 
main_function 2>&1 | tee -a /var/log/my_uber_script.log 
+0

Meinten Sie main_function >> /var/log/my_uber_script.log 2> & 1 –

+0

Ich benutze main_function in einer solchen Leitung. In diesem Fall gibt Ihr Skript jedoch nicht den ursprünglichen Rückgabewert zurück. In bash sollte man dann mit 'exit $ {PIPESTATUS [0]}' beenden. – rudimeier

87

Adressierung der Frage als aktualisiert.

#...part of script without redirection... 

{ 
    #...part of script with redirection... 
} > file1 2>file2 # ...and others as appropriate... 

#...residue of script without redirection... 

Die geschweiften Klammern "{...}" stellen eine Einheit der E/A-Umleitung bereit. Die geschweiften Klammern müssen dort erscheinen, wo ein Befehl erscheinen könnte - vereinfacht, am Anfang einer Zeile oder nach einem Semikolon. (Ja, das kann präzisiert werden, wenn Sie streiten wollen, lassen Sie mich wissen.)

Sie haben Recht, dass die originale stdout und stderr mit den Umleitungen bewahren können Sie zeigte, aber es ist in der Regel einfacher Für die Personen, die das Skript später pflegen müssen, um zu verstehen, was passiert, wenn Sie den umgeleiteten Code wie oben gezeigt definieren.

+3

Dies ist viel klarer, als die ursprünglichen Deskriptoren zu speichern und später wiederherzustellen. –

+13

Ich musste etwas googeln, um zu verstehen, was das wirklich macht, also wollte ich teilen. Die geschweiften Klammern werden zu einem ["Block of code"] (http://www.tldp.org/LDP/abs/html/special-chars.html#CODEBLOCKREF), der im Prinzip eine _anonyme Funktion_ erzeugt. Die Ausgabe alles im Codeblock kann dann umgeleitet werden (Siehe ** Beispiel 3-2 ** von diesem Link). Beachten Sie auch, dass die geschweiften Klammern _DO eine [Subshell] (http://www.tldp.org/LDP/abs/html/subshells.html), aber ähnlich [I/O-Umleitungen] (http not_ zu starten: // www .tldp.org/LDP/abs/html/ioredirintro.html) kann mit Subshells mit Klammern gemacht werden. – chris

+1

Ich mag diese Lösung besser als die anderen.Selbst eine Person, die nur das grundlegendste Verständnis für die E/A-Umleitung hat, kann verstehen, was passiert. Plus, es ist ausführlicher. Und als Pythoner liebe ich ausführlich. –

2
[ -t <&0 ] || exec >> test.log 
2

Für die ursprüngliche stdout Speichern und stderr Sie verwenden können:

exec [fd number]<&1 
exec [fd number]<&2 

Zum Beispiel wird der folgende Code "walla1" drucken und "walla2" in die Protokolldatei (a.txt), „walla3 "zu stout," walla4 "zu stderr.

#!/bin/bash 

exec 5<&1 
exec 6<&2 

exec 1> ~/a.txt 2>&1 

echo "walla1" 
echo "walla2" >&2 
echo "walla3" >&5 
echo "walla4" >&6 
+1

Normalerweise ist es besser, 'exec 5> & 1' und' exec 6> & 2' zu verwenden, wobei die Ausgabeumleitungsnotation anstelle der Eingabeumleitungsnotation für die Ausgaben verwendet wird. Sie kommen damit durch, denn wenn das Skript von einem Terminal ausgeführt wird, ist die Standardeingabe auch beschreibbar und sowohl die Standardausgabe als auch der Standardfehler sind lesbar (oder ist es "Laster"?) Einer historischen Eigenart: das Terminal ist für geöffnet Lesen und Schreiben und das gleiche [Dateibeschreibung öffnen] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html) wird für alle drei Standard-I/O-Dateideskriptoren verwendet. –

Verwandte Themen