Écriture des sorties dans le fichier journal et la console

99

Dans le shell Unix, j'ai un fichier env (le fichier env définit les paramètres requis pour exécuter le script utilisateur comme le nom et le chemin du fichier journal, redirige les sorties et les erreurs vers le fichier journal, les détails de connexion à la base de données, etc. ) qui redirige toutes les sorties ( messages d'écho ) et les erreurs dans le fichier journal à partir du script exécuté à l'aide du code suivant:

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

Le fichier env est exécuté au début de chaque script. En raison du code ci-dessus dans le fichier env, toutes les sorties de la console qui pourraient être des sorties utilisateur ou des erreurs sont directement sorties dans le fichier journal, ce dont j'avais réellement besoin.

Mais il existe des sorties utilisateur sélectives que je souhaite afficher à la fois dans la console et dans le fichier journal. Mais à cause du code ci-dessus, je ne suis pas en mesure de le faire.

Je sais que si je supprime le code ci-dessus, je peux obtenir le résultat souhaité dans ce cas, mais je devrai écrire manuellement toutes les autres sorties dans le fichier journal, ce qui n'est pas une tâche facile.

Existe-t-il un moyen d'obtenir la sortie à la fois dans la console et dans le fichier journal sans supprimer les codes ci-dessus?

abinash shrestha
la source

Réponses:

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

enverrait la sortie stdout et stderr dans le fichier journal, mais vous laisserait également avec fd 3 connecté à la console, vous pouvez donc le faire

echo "Some console message" 1>&3

pour écrire un message uniquement sur la console, ou

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

pour écrire un message à la fois dans la console et dans le fichier journal - teeenvoie sa sortie à la fois à son propre fd 1 (qui est ici le LOG_FILE) et au fichier dans lequel vous lui avez dit d'écrire (qui est ici fd 3, c'est-à-dire la console).

Exemple:

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

imprimerait

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

sur la console et mettez

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

dans le fichier journal.

Ian Roberts
la source
2
Cela a fonctionné comme vous l'avez suggéré. Mais je n'ai pas compris tee / dev / fd / 3 . Je sais que tee écrit un message dans le fichier journal et la console, mais je n'ai pas compris exactement le / dev / fd / 3 utilisé après le tee
abinash shrestha
@shrestha c'est une utilisation inhabituelle du tee, je suis d'accord. Le /dev/fd/3est un nom de fichier qui fait référence à "le fd 3 actuellement ouvert", donc tee /dev/fd/3écrira tout ce qui arrive sur son stdin dans fd 1 et aussi fd 3 (le /dev/fd/3fichier). Fd 1 est connecté au fichier journal, fd 3 est connecté à la console.
Ian Roberts
Aussi, vous voulez seulement écrire dans un fichier et non dans la console essayez: echo "blah" | tee file1.txt | tee file2.txt >/dev/null 'Blah' ne sera pas mis dans file1.txt & file2.txt, mais pas écrit sur la console.
danger89 le
Cela m'a été très utile. Bien que j'ai remarqué quelque chose qui pourrait être hors sujet, mais peut-être que certains d'entre vous en connaissent les raisons. Im exécutant des scripts R à partir d'un script bash. La sortie console des différentes fonctions R est colorée (comme je l'ai définie). lors de l'utilisation de l'approche ci-dessus pour rediriger la sortie vers la console ET le fichier journal, la sortie de la console n'est plus colorée. Quelle peut être la raison de cela?
dieHellste
1
@dieHellste certains programmes sont capables de détecter lorsque leur sortie est dirigée vers un autre processus (dans ce cas tee, qui à son tour écrit sur le terminal) plutôt que d'aller directement vers un terminal, et d'ajuster leur sortie pour correspondre.
Ian Roberts
43

Oui, vous souhaitez utiliser tee:

tee - lire à partir de l'entrée standard et écrire dans la sortie et les fichiers standard

Dirigez simplement votre commande vers le tee et passez le fichier comme argument, comme ceci:

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

Cela imprime à la fois la sortie sur le STDOUT et écrit la même sortie dans un fichier journal. Voir man teepour plus d'informations.

Notez que cela n'écrira pas stderr dans le fichier journal, donc si vous souhaitez combiner les deux flux, utilisez:

exec 1 2>&1 | tee ${LOG_FILE}
Jon Cairns
la source
2
La solution ci-dessus n'a pas fonctionné. J'ai le fichier redirect.env comme: ##### 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} ######### et le fichier de travail contenait le code suivant: ##### output.sh ##### #! / bin / sh. redirect.env echo "Sortie valide" ech "sortie invalide" ############## mais j'obtiens l'erreur: #### redirect.env: ligne 3: exec: 1: introuvable redirect.env: ligne 5: exec: 1: non trouvé redirect.env: ligne 6: exec: 2: non trouvé #### et dans le fichier journal, j'obtiens également la même erreur. Est-ce que je fais quelque chose de mal?
abinash shrestha
C'est très difficile à dire, car les nouvelles lignes sont supprimées dans votre commentaire. Pourriez-vous ajouter une pâte du code quelque part comme l' essentiel ?
Jon Cairns
1
J'ai ajouté les fichiers au lien UnixRedirect . Les fichiers associés sont redirect.env et output.sh
abinash shrestha
1
Ce code ne semble pas fonctionner (du moins plus). Vous obtiendrez habituellementscript.sh: line 5: exec: 1: not found
tftd
39

J'ai essayé la réponse de Joonty, mais j'ai aussi

exec: 1: introuvable

Erreur. C'est ce qui fonctionne le mieux pour moi ( confirmé pour fonctionner également dans zsh):

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

Le fichier /tmp/both.log contient ensuite

this is stdout
chmmm command not found 

Le fichier /tmp/both.log est ajouté à moins que vous ne supprimiez le -a du tee.

Allusion: >(...) est une substitution de processus. Il laisse passer execla teecommande comme s'il s'agissait d'un fichier.

Alfonx
la source
1
Cela a fonctionné comme un charme pour moi, alors que les autres réponses ont été aléatoires.
Jay Taylor
Merci d'avoir partagé cet extrait. Cela semble fonctionner très bien (par rapport aux autres réponses)!
tftd
Cela fonctionne, mais maintenant mon shell est différent après l'exécution.
Josh Usre
@JoshUsre Si vous voulez de l'aide ou si vous voulez aider d'autres utilisateurs SO, veuillez expliquer ce qui est différent, et décrire votre shell, version, système d'exploitation, etc.
alfonx
1
Si vous n'avez pas besoin de faire la différence entre stdout et stderr, vous pouvez combiner les instructions avec exec > >(tee ${LOG_FILE}) 2>&1.
darkdragon
5

Je voulais afficher les journaux sur stdout et le fichier journal avec l'horodatage. Aucune des réponses ci-dessus n'a fonctionné pour moi. J'ai utilisé la substitution de processus et la commande exec et j'ai trouvé le code suivant. Exemples de journaux:

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

Ajoutez les lignes suivantes en haut de votre script:

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)

J'espère que cela aide quelqu'un!

Jyoti Dhiman
la source
Est-il possible de consigner toutes les erreurs d'application pour se connecter au répertoire hôte afin que cela aide devops ....
Prasad Shinde
3

pour le fichier journal, vous pouvez dater de saisir des données texte. le code suivant peut aider

# 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  

sortie: 2. Le fichier journal sera créé lors de la première exécution et continuera à être mis à jour à partir des prochaines exécutions. En cas de fichier journal manquant lors de l'exécution future, le script créera un nouveau fichier journal.

user2197712
la source
1

J'ai trouvé un moyen d'obtenir le résultat souhaité. Bien que ce soit de manière quelque peu peu orthodoxe. Quoi qu'il en soit, c'est parti. Dans le fichier redir.env, j'ai le code suivant:

#####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}
    }

Ensuite, dans le script réel, j'ai les codes suivants:

#!/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

Ici, les sorties d' écho uniquement vers la console, les sorties de journaux uniquement vers le fichier journal et les sorties de messages vers le fichier journal et la console.

Après avoir exécuté le fichier de script ci-dessus, j'ai les sorties suivantes:

Dans la console

Dans la console
Écho sur la console uniquement
Pour la console et le journal

Pour le fichier journal

Dans le fichier journal Écrit dans le fichier journal uniquement
Il s'agit de stderr. Écrit dans le fichier journal uniquement
Pour la console et le journal

J'espère que cette aide.

abinash shrestha
la source
1

Essayez ceci, cela fera le travail:

log_file=$curr_dir/log_file.txt
exec > >(tee -a ${log_file} )
exec 2> >(tee -a ${log_file} >&2)
amousa
la source
Cela exec > >(tee -a ${log_file} )fonctionne parfaitement pour mes besoins. Les solutions précédentes ci-dessus interrompraient des parties de mon script qui forceraient la sortie en cas d'échec. Merci
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
la source
0

Je trouve très utile d'ajouter à la fois stdout et stderr à un fichier journal. J'étais content de voir une solution par alfonx avec exec > >(tee -a), car je me demandais comment accomplir cela en utilisant exec. Je suis tombé sur une solution créative utilisant la syntaxe here-doc et .: /unix/80707/how-to-output-text-to-both-screen-and-file-inside-a-shell -scénario

J'ai découvert que dans zsh, la solution here-doc peut être modifiée en utilisant la construction "multios" pour copier la sortie à la fois vers stdout / stderr et le fichier journal:

#!/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

Elle n'est pas aussi lisible que la execsolution mais elle a l'avantage de vous permettre de ne consigner qu'une partie du script. Bien sûr, si vous omettez l'EOF, tout le script est exécuté avec la journalisation. Je ne sais pas comment zshimplémente multios, mais il peut avoir moins de frais généraux que tee. Malheureusement, il semble que l'on ne puisse pas utiliser multios avec exec.

Métamorphique
la source