Schreiben von Ausgaben in Protokolldatei und Konsole

97

In Unix - Shell, habe ich eine env - Datei ( env - Datei die Parameter definiert , für den Betrieb des Benutzerskript wie Protokolldateinamen und den Pfad erforderlich, Umleitung Ausgänge und Fehler - Datei, Datenbankverbindungsdaten, etc. zu protokollieren ) , das leitet alle die Ausgänge ( Echo - Nachrichten ) und Fehler in der Protokolldatei aus dem ausgeführten Skript mit dem folgenden Code:

exec 1>>${LOG_FILE}
exec 2>>${LOG_FILE}

Die env-Datei wird am Anfang jedes Skripts ausgeführt. Aufgrund des obigen Codes in der env-Datei werden alle Konsolenausgaben, bei denen es sich möglicherweise um Benutzerausgaben oder Fehler handelt, direkt in die Protokolldatei ausgegeben, was ich tatsächlich benötigte.

Es gibt jedoch einige selektive Benutzerausgaben, die sowohl in der Konsole als auch in der Protokolldatei angezeigt werden sollen. Aber aufgrund des obigen Codes kann ich das nicht.

Ich weiß, dass ich, wenn ich den obigen Code entferne, das gewünschte Ergebnis für diesen Fall erzielen kann, aber ich muss alle anderen Ausgaben manuell in die Protokolldatei schreiben, was keine leichte Aufgabe ist.

Gibt es eine Möglichkeit, die Ausgabe sowohl in der Konsole als auch in der Protokolldatei abzurufen, ohne die obigen Codes zu entfernen?

Abinash Shrestha
quelle

Antworten:

105
exec 3>&1 1>>${LOG_FILE} 2>&1

würde stdout und stderr Ausgabe in die Protokolldatei senden, würde aber auch fd 3 mit der Konsole verbunden lassen, so dass Sie tun können

echo "Some console message" 1>&3

um eine Nachricht nur an die Konsole zu schreiben, oder

echo "Some console and log file message" | tee /dev/fd/3

Um eine Nachricht sowohl an die Konsole als auch an die Protokolldatei zu schreiben, wird die teeAusgabe sowohl an die eigene fd 1 (die hier die ist LOG_FILE) als auch an die Datei gesendet, in die Sie schreiben sollen (die hier fd 3 ist, dh an die Konsole).

Beispiel:

exec 3>&1 1>>${LOG_FILE} 2>&1

echo "This is stdout"
echo "This is stderr" 1>&2
echo "This is the console (fd 3)" 1>&3
echo "This is both the log and the console" | tee /dev/fd/3

würde drucken

This is the console (fd 3)
This is both the log and the console

auf die Konsole und setzen

This is stdout
This is stderr
This is both the log and the console

in die Protokolldatei.

Ian Roberts
quelle
2
Es hat genau so funktioniert, wie Sie es vorgeschlagen haben. Aber ich habe tee / dev / fd / 3 nicht verstanden . Ich weiß , dass T - Shirt Nachricht schreibt Datei und Konsole anmelden, aber ich habe nicht genau verstehen , die / dev / fd / 3 nach T - Stück verwendet
Abinash shrestha
@shrestha es ist eine ungewöhnliche Verwendung von Tee, ich stimme zu. Das /dev/fd/3ist ein Dateiname, der sich auf "das aktuell geöffnete fd 3" bezieht, also tee /dev/fd/3schreibt alles, was auf seinem stdin ankommt, in fd 1 und auch in fd 3 (die /dev/fd/3Datei). Fd 1 ist mit der Protokolldatei verbunden, Fd 3 ist mit der Konsole verbunden.
Ian Roberts
Außerdem möchten Sie nur in eine Datei schreiben und nicht in die Konsole. Versuchen Sie: echo "blah" | tee file1.txt | tee file2.txt >/dev/null 'Blah' wird nicht in file1.txt & file2.txt eingefügt, sondern nicht in die Konsole geschrieben.
Gefahr89
Das war sehr hilfreich für mich. Ich habe zwar etwas bemerkt, das möglicherweise nicht zum Thema gehört, aber vielleicht kennen einige von Ihnen die Gründe dafür. Ich führe R-Skripte aus einem Bash-Skript aus. Die Konsolenausgabe verschiedener R-Funktionen ist farbig (wie ich es definiert habe). Wenn Sie den obigen Ansatz verwenden, um die Ausgabe in die Konsolen- UND Protokolldatei umzuleiten, ist die Konsolenausgabe nicht mehr farbig. Was könnte der Grund dafür sein?
dieHellste
1
@dieHellste Einige Programme können erkennen, wann ihre Ausgabe an einen anderen Prozess weitergeleitet wird (in diesem Fall tee, der wiederum auf das Terminal schreibt), anstatt direkt auf ein Terminal zuzugreifen, und ihre Ausgabe entsprechend anpassen.
Ian Roberts
43

Ja, Sie möchten verwenden tee:

Tee - Lesen Sie von der Standardeingabe und schreiben Sie in die Standardausgabe und -dateien

Leiten Sie einfach Ihren Befehl an tee und übergeben Sie die Datei als Argument, wie folgt:

exec 1 | tee ${LOG_FILE}
exec 2 | tee ${LOG_FILE}

Dies druckt die Ausgabe in STDOUT und schreibt dieselbe Ausgabe in eine Protokolldatei. Siehe man teefür weitere Informationen.

Beachten Sie, dass dadurch kein stderr in die Protokolldatei geschrieben wird. Wenn Sie also die beiden Streams kombinieren möchten, verwenden Sie:

exec 1 2>&1 | tee ${LOG_FILE}
Jon Cairns
quelle
2
Die obige Lösung hat nicht funktioniert. Ich habe die Datei redirect.env wie folgt: ##### redirect.env ###### export LOG_FILE = log.txt exec 1 2> & 1 | tee -a $ {LOG_FILE} exec 1 | tee -a $ {LOG_FILE} exec 2 | tee -a $ {LOG_FILE} ######### und die Arbeitsdatei enthielten folgenden Code: ##### output.sh ##### #! / bin / sh. redirect.env echo "Gültige Ausgabe" ech "ungültige Ausgabe" ############### aber ich erhalte die Fehlermeldung: #### redirect.env: Zeile 3: exec: 1: nicht gefunden redirect.env: Zeile 5: exec: 1: nicht gefunden redirect.env: Zeile 6: exec: 2: nicht gefunden #### und in der Protokolldatei wird der gleiche Fehler angezeigt. Mache ich etwas falsch
Abinash Shrestha
Es ist sehr schwer zu sagen, da die neuen Zeilen in Ihrem Kommentar entfernt werden. Könnten Sie eine Einfügung des Codes an einer Stelle wie Gist hinzufügen ?
Jon Cairns
1
Ich habe die Dateien zum Link UnixRedirect hinzugefügt . Die zugehörigen Dateien sind redirect.env und output.sh
abinash shrestha
1
Dieser Code scheint nicht zu funktionieren (zumindest nicht mehr). Sie würden normalerweisescript.sh: line 5: exec: 1: not found
tftd
37

Ich habe versucht, Joonty zu antworten, aber ich habe auch die bekommen

exec: 1: nicht gefunden

Error. Dies ist, was für mich am besten funktioniert ( bestätigt , dass es auch in zsh funktioniert):

#!/bin/bash
LOG_FILE=/tmp/both.log
exec > >(tee ${LOG_FILE}) 2>&1
echo "this is stdout"
chmmm 77 /makeError

Die Datei /tmp/both.log enthält anschließend

this is stdout
chmmm command not found 

Das /tmp/both.log wird angehängt, es sei denn, Sie entfernen das -a vom Tee.

Hinweis: >(...)ist eine Prozesssubstitution. Damit kann execder teeBefehl wie eine Datei ausgeführt werden.

Alfonx
quelle
1
Das hat für mich wie ein Zauber gewirkt, während die anderen Antworten getroffen oder verfehlt wurden.
Jay Taylor
Vielen Dank, dass Sie dieses Snippet geteilt haben. Dies scheint gut zu funktionieren (im Vergleich zu den anderen Antworten)!
tftd
Das funktioniert, aber jetzt ist meine Shell nach der Ausführung anders.
Josh Usre
@JoshUsre Wenn Sie Hilfe benötigen oder anderen SO-Benutzern helfen möchten, erklären Sie bitte, was anders ist, und beschreiben Sie Ihre Shell, Version, Betriebssystem usw.
Alfonx
1
Wenn Sie nicht zwischen stdout und stderr unterscheiden müssen, können Sie die Anweisungen mit kombinieren exec > >(tee ${LOG_FILE}) 2>&1.
Darkdragon
5

Ich wollte Protokolle auf stdout und Protokolldatei zusammen mit dem Zeitstempel anzeigen. Keine der oben genannten Antworten hat bei mir funktioniert. Ich habe die Prozessersetzung und den Befehl exec verwendet und den folgenden Code gefunden. Beispielprotokolle:

2017-06-21 11:16:41+05:30 Fetching information about files in the directory...

Fügen Sie oben in Ihrem Skript die folgenden Zeilen hinzu:

LOG_FILE=script.log
exec > >(while read -r line; do printf '%s %s\n' "$(date --rfc-3339=seconds)" "$line" | tee -a $LOG_FILE; done)
exec 2> >(while read -r line; do printf '%s %s\n' "$(date --rfc-3339=seconds)" "$line" | tee -a $LOG_FILE; done >&2)

Hoffe das hilft jemandem!

Jyoti Dhiman
quelle
Ist es möglich, alle Anwendungsfehler zu protokollieren, um sich im Host-Verzeichnis anzumelden, damit es den Entwicklern hilft?
Prasad Shinde
3

Für die Protokolldatei können Sie ein Datum eingeben, um Textdaten einzugeben. Der folgende Code kann helfen

# declaring variables

Logfile="logfile.txt"   
MAIL_LOG="Message to print in log file"  
Location="were is u want to store log file"

cd $Location   
if [ -f $Logfile ]  
then   
echo "$MAIL_LOG " >> $Logfile

else        

touch $Logfile   
echo "$MAIL_LOG" >> $Logfile    

fi  

Ausgabe: 2. Die Protokolldatei wird beim ersten Durchlauf erstellt und wird bei den nächsten Durchläufen weiter aktualisiert. Falls die Protokolldatei bei einer zukünftigen Ausführung fehlt, erstellt das Skript eine neue Protokolldatei.

user2197712
quelle
1

Ich habe einen Weg gefunden, um die gewünschte Ausgabe zu erhalten. Obwohl es etwas unorthodox sein kann. Sowieso hier geht es. In der Datei redir.env habe ich folgenden Code:

#####redir.env#####    
export LOG_FILE=log.txt

      exec 2>>${LOG_FILE}

    function log {
     echo "$1">>${LOG_FILE}
    }

    function message {
     echo "$1"
     echo "$1">>${LOG_FILE}
    }

Dann habe ich im eigentlichen Skript folgende Codes:

#!/bin/sh 
. redir.env
echo "Echoed to console only"
log "Written to log file only"
message "To console and log"
echo "This is stderr. Written to log file only" 1>&2

Hier Echo Ausgänge nur Konsole einzuloggen Ausgänge nur Datei und die Protokollmeldung Ausgänge sowohl an der Protokolldatei und Konsole.

Nach dem Ausführen der obigen Skriptdatei habe ich folgende Ausgaben:

In der Konsole

In der Konsole
Nur zur Konsole Echo
Zum Konsolen und Protokollieren

Für die Protokolldatei

In Protokolldatei Nur in Protokolldatei geschrieben
Dies ist stderr. Nur in Protokolldatei geschrieben
Zur Konsole und Protokollierung

Ich hoffe das hilft.

Abinash Shrestha
quelle
1

Versuchen Sie dies, es wird die Arbeit erledigen:

log_file=$curr_dir/log_file.txt
exec > >(tee -a ${log_file} )
exec 2> >(tee -a ${log_file} >&2)
amousa
quelle
Dies exec > >(tee -a ${log_file} )funktioniert perfekt für meine Bedürfnisse. Frühere Lösungen oben haben Teile meines Skripts unterbrochen, die das Beenden erzwingen, wenn sie fehlschlagen. Vielen Dank
MitchellK
1
    #
    #------------------------------------------------------------------------------
    # echo pass params and print them to a log file and terminal
    # with timestamp and $host_name and $0 PID
    # usage:
    # doLog "INFO some info message"
    # doLog "DEBUG some debug message"
    # doLog "WARN some warning message"
    # doLog "ERROR some really ERROR message"
    # doLog "FATAL some really fatal message"
    #------------------------------------------------------------------------------
    doLog(){
        type_of_msg=$(echo $*|cut -d" " -f1)
        msg=$(echo "$*"|cut -d" " -f2-)
        [[ $type_of_msg == DEBUG ]] && [[ $do_print_debug_msgs -ne 1 ]] && return
        [[ $type_of_msg == INFO ]] && type_of_msg="INFO " # one space for aligning
        [[ $type_of_msg == WARN ]] && type_of_msg="WARN " # as well

        # print to the terminal if we have one
        test -t 1 && echo " [$type_of_msg] `date "+%Y.%m.%d-%H:%M:%S %Z"` [$run_unit][@$host_name] [$$] ""$msg"

        # define default log file none specified in cnf file
        test -z $log_file && \
            mkdir -p $product_instance_dir/dat/log/bash && \
                log_file="$product_instance_dir/dat/log/bash/$run_unit.`date "+%Y%m"`.log"
        echo " [$type_of_msg] `date "+%Y.%m.%d-%H:%M:%S %Z"` [$run_unit][@$host_name] [$$] ""$msg" >> $log_file
    }
    #eof func doLog
Yordan Georgiev
quelle
0

Ich finde es sehr nützlich, sowohl stdout als auch stderr an eine Protokolldatei anzuhängen. Ich war froh, eine Lösung von alfonx mit zu sehen exec > >(tee -a), weil ich mich fragte, wie ich dies mit erreichen könnte exec. Ich bin auf eine kreative Lösung gestoßen, die die Syntax von here-doc verwendet und .: /unix/80707/how-to-output-text-to-both-screen-and-file-inside-a-shell -Skript

Ich habe festgestellt, dass in zsh die Here-Doc-Lösung mithilfe des Konstrukts "multios" geändert werden kann, um die Ausgabe sowohl in stdout / stderr als auch in die Protokolldatei zu kopieren:

#!/bin/zsh
LOG=$0.log
# 8 is an arbitrary number;
# multiple redirects for the same file descriptor 
# triggers "multios"
. 8<<\EOF /dev/fd/8 2>&2 >&1 2>>$LOG >>$LOG
# some commands
date >&2
set -x
echo hi
echo bye
EOF
echo not logged

Es ist nicht so lesbar wie die execLösung, bietet jedoch den Vorteil, dass Sie nur einen Teil des Skripts protokollieren können. Wenn Sie die EOF weglassen, wird das gesamte Skript natürlich mit Protokollierung ausgeführt. Ich bin nicht sicher, wie zshMultios implementiert werden, aber es kann weniger Overhead haben als tee. Leider scheint es, dass man mit Multios nicht arbeiten kann exec.

Metamorph
quelle