web-dev-qa-db-fra.com

Redirige toutes les commandes suivantes stderr en utilisant exec

J'ai un fichier bash dont j'ai besoin pour rediriger toutes les sorties vers un seul fichier, le journal de débogage ainsi que vers le terminal. Je dois rediriger à la fois stdout et stderr vers le débogage et l'enregistrer pour toutes les commandes du script.

Je ne veux pas ajouter 2>&1 | tee -a $DEBUG pour chaque commande du fichier. Je pourrais vivre avec | tee -a $DEBUG.

Je me souviens qu'il y avait un moyen de le faire avec quelque chose comme exec 2>&1.

Actuellement, j'utilise quelque chose comme ceci:

#!/bin/bash
DEBUGLOG=/tmp/debug
exec 2>&1
somecommand | tee -a $DEBUGLOG
somecommand2 | tee -a $DEBUGLOG
somecommand3 | tee -a $DEBUGLOG

mais ça ne marche pas. Quelqu'un at-il une solution/peut-il expliquer la cause?

45
Avi

Quant à une solution pour rediriger beaucoup de commandes à la fois:

#!/bin/bash
{
    somecommand 
    somecommand2
    somecommand3
} 2>&1 | tee -a $DEBUGLOG

Pourquoi votre solution d'origine ne fonctionne pas: exec 2> & 1 redirigera la sortie d'erreur standard vers la sortie standard de votre Shell, qui, si vous exécutez votre script à partir de la console, sera votre console. la redirection de canal sur les commandes ne redirigera que la sortie standard de la commande.

Du point de vue de somecommand, sa sortie standard va dans un tube connecté à tee et l'erreur standard va dans le même fichier/pseudofile que l'erreur standard du Shell, que vous redirigez à la sortie standard du Shell, qui sera la console si vous exécutez votre programme à partir de la console.

La seule vraie façon de l'expliquer est de voir ce qui se passe vraiment:

L'environnement d'origine de votre shell pourrait ressembler à ceci si vous l'exécutez à partir du terminal:

stdin -> /dev/pts/42
stdout -> /dev/pts/42
stderr -> /dev/pts/42

Après avoir redirigé l'erreur standard vers la sortie standard (exec 2>&1), vous ... ne changez rien. Mais si vous redirigez la sortie standard du script vers un fichier, vous vous retrouveriez avec un environnement comme celui-ci:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /dev/pts/42

Ensuite, la redirection de l'erreur standard de Shell vers la sortie standard se terminerait comme suit:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /your/file

L'exécution d'une commande héritera de cet environnement. Si vous exécutez une commande et la redirigez vers tee, l'environnement de la commande serait:

stdin -> /dev/pts/42
stdout -> pipe:[4242]
stderr -> /your/file

L'erreur standard de votre commande va donc toujours dans ce que le Shell utilise comme erreur standard.

Vous pouvez réellement voir l'environnement d'une commande en regardant dans /proc/[pid]/fd: utilisation ls -l pour lister également le contenu du lien symbolique. Le 0 le fichier ici est une entrée standard, 1 est la sortie standard et 2 est l'erreur standard. Si la commande ouvre plus de fichiers (et la plupart des programmes le font), vous les verrez également. Un programme peut également choisir de rediriger ou de fermer son entrée/sortie standard et de réutiliser 0, 1 et 2.

42
BatchyX

Vous pouvez utiliser exec comme ceci en haut de votre script:

exec > >(tee "$HOME/somefile.log") 2>&1

Par exemple:

#!/bin/bash -

exec > >(tee "$HOME/somefile.log") 2>&1

echo "$HOME"
echo hi
date
date +%F
echo bye 1>&2

Me donne une sortie dans le fichier $HOME/somefile.log et au terminal comme ceci:

/home/saml
hi
Sun Jan 20 13:54:17 EST 2013
2013-01-20
bye
43
slm

Écrire stderr et stdout dans un fichier, afficher stderr à l'écran (sur stdout)

exec 2> >(tee -a -i "$HOME/somefile.log")
exec >> "$HOME/somefile.log"

Utile pour crons, vous pouvez donc recevoir des erreurs (et seulement des erreurs) par mail

0
Lluís