164 votes

Comment puis-je envoyer à la fois STDOUT et STDERR vers le terminal et un fichier journal ?

Je dispose d'un script qui sera exécuté de manière interactive par des utilisateurs non techniques. Le script écrit des mises à jour d'état vers STDOUT afin que l'utilisateur puisse être sûr que le script fonctionne correctement.

Je veux à la fois rediriger STDOUT et STDERR vers le terminal (pour que l'utilisateur puisse voir que le script fonctionne ainsi que voir s'il y a un problème). Je veux également rediriger les deux flux vers un fichier journal.

J'ai vu un tas de solutions sur le net. Certains ne fonctionnent pas et d'autres sont terriblement compliqués. J'ai développé une solution fonctionnelle (que je vais entrer comme une réponse), mais c'est bricolé.

La solution parfaite serait une seule ligne de code qui pourrait être incorporée au début de n'importe quel script envoyant les deux flux à la fois au terminal et à un fichier journal.

MODIFIER : Rediriger STDERR vers STDOUT et diriger le résultat vers tee fonctionne, mais cela dépend du fait que les utilisateurs se souviennent de rediriger et d'acheminer la sortie. Je veux que le journal soit idiot et automatique (c'est pourquoi j'aimerais être capable d'incorporer la solution dans le script lui-même.)

0 votes

Pour les autres lecteurs : question similaire : stackoverflow.com/questions/692000/…

4voto

tvanfosson Points 268301

Utilisez le programme tee et dupliquez stderr vers stdout.

 programme 2>&1 | tee > fichier_journal

4voto

Paul Tomblin Points 83687

Si ces scripts vous appartiennent et qu'il s'agit de scripts bash, vous pouvez entourer l'ensemble du contenu avec

  {
    votre script
  } 2>&1 | tee foo

3voto

gnud Points 26854

Utilisez la commande script dans votre script (man 1 script)

Créez un script shell enveloppeur (2 lignes) qui configure script() puis appelle exit.

Partie 1: wrap.sh

#!/bin/sh
script -c './realscript.sh'
exit

Partie 2: realscript.sh

#!/bin/sh
echo 'Output'

Résultat:

~: sh wrap.sh 
Script started, file is typescript
Output
Script done, file is typescript
~: cat typescript 
Script started on fr. 12. des. 2008 kl. 18.07 +0100
Output

Script done on fr. 12. des. 2008 kl. 18.07 +0100
~:

1voto

JPLemme Points 1312

J'ai créé un script appelé "RunScript.sh". Le contenu de ce script est :

${APP_HOME}/${1}.sh ${2} ${3} ${4} ${5} ${6} 2>&1 | tee -a ${APP_HOME}/${1}.log

Je l'appelle comme ceci :

./RunScript.sh ScriptToRun Param1 Param2 Param3 ...

Cela fonctionne, mais cela nécessite que les scripts de l'application soient exécutés via un script externe. C'est un peu astucieux.

9 votes

Vous perdrez le regroupement des arguments contenant des espaces blancs avec $1 $2 $3 ..., vous devriez utiliser (avec des guillemets): "$@"

1voto

denis Points 7316

Un an plus tard, voici un ancien script bash pour enregistrer n'importe quoi. Par exemple,
teelog make ... enregistre dans un nom de fichier journal généré (et voir l'astuce pour enregistrer les make imbriqués aussi.)

#!/bin/bash
me=teelog
Version="2008-10-9 oct denis-bz"

Aide() {
cat <&1 | tee `day`-command-args.log

C'est-à-dire, stdout et stderr vont à la fois à l'écran et dans un fichier journal.
(La commande Unix "tee" est nommée d'après les raccords en T, 1 entrée -> 2 sorties ;
voir http://en.wikipedia.org/wiki/Tee_(command) ).

Le nom de fichier journal par défaut est composé de "command" et de tous les "args" :
    $me cmd -opt dir/file enregistre dans `day`-cmd--opt-file.log .
Pour enregistrer dans xx.log à la place, exporter log=xx.log ou
    $me log=xx.log cmd ...
Si "logdir" est défini, les journaux sont placés dans ce répertoire, qui doit exister.
Un ancien xx.log est déplacé vers /tmp/\$USER-xx.log .

Le fichier journal a un en-tête comme
    # from: command args ...
    # run: date pwd etc.
pour montrer ce qui a été exécuté ; voir "From" dans ce fichier.

Appelé comme "Log" (ln -s $me Log), Log anycommand ... enregistre dans un fichier :
    command args ... > `day`-command-args.log
et redirige stderr à la fois vers le fichier journal et le terminal -- bash seulement.

Certaines commandes qui demandent une entrée depuis la console, comme un mot de passe,
ne demandent pas si elles "| tee" ; vous ne pouvez que taper à l'avance, avec précaution.

Pour enregistrer tous les "make", y compris les imbriqués comme
    cd dir1; \$(MAKE)
    cd dir2; \$(MAKE)
    ...
export MAKE="$me make"

!
  # Voir aussi : enregistrement de sortie dans screen(1).
    exit 1
}

#-------------------------------------------------------------------------------
# bzutil.sh  denisbz mai 2008 --

day() {  # 30mar, 3mar
    /bin/date +%e%h  |  tr '[A-Z]' '[a-z]'  |  tr -d ' '
}

edate() {  # 19 mai 2008 15:56
    echo `/bin/date "+%e %h %Y %H:%M"`
}

From() {  # en-tête  # from: $*  # run: date pwd ...
    case `uname` in Darwin )
        mac=" mac `sw_vers -productVersion`"
    esac
    cut -c -200 <= 100 ))  &&  break  # longueur max 100
    done
            # pas d'espaces etc dans le nom du journal s'il vous plaît, les remplacer par "-"
    echo $logdir/` echo "$log".log  |  tr -C '.:+=[:alnum:]_\n' - `
}

#-------------------------------------------------------------------------------
case "$1" in
-v* | --v* )
    echo "$0 version : $Version"
    exit 1 ;;
"" | -* )
    Aide
esac

    # balayer log= etc --
while [[ $1 == [a-zA-Z_]*=* ]]; do
    export "$1"
    shift
done

: ${logdir=.}
[[ -w $logdir ]] || {
    echo >&2 "erreur : $me : impossible d'écrire dans logdir $logdir"
    exit 1
    }
: ${log=` logfilename "$@" `}
[[ -f $log ]]  &&
    /bin/mv "$log" "/tmp/$USER-${log##*/}"

case ${0##*/} in  # basename
log | Log )  # à la fois vers le journal, stderr aussi vers stderr de l'appelant --
{
    From "$@"
    "$@"
} > $log  2> >(tee /dev/stderr)  # bash seulement
    # voir http://wooledge.org:8000/BashFAQ 47, stderr vers un tuyau
;;

* )
#-------------------------------------------------------------------------------
{
    From "$@"  # en-tête : de ... date pwd etc.

    "$@"  2>&1  # exécuter la commande avec stderr et stdout à la fois vers le journal

} | tee $log
    # mac tee met en mémoire tampon stdout ?

esac

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X