723 votes

Comment puis-je écrire l'erreur standard dans un fichier en utilisant "tee" avec un tuyau ?

Je sais comment utiliser tee pour écrire la sortie ( sortie standard ) de aaa.sh à bbb.out tout en l'affichant dans le terminal :

./aaa.sh | tee bbb.out

Comment pourrais-je maintenant écrire aussi erreur standard dans un fichier nommé ccc.out tout en l'affichant ?

2 votes

Pour clarifier - voulez-vous que stderr aille à l'écran aussi bien qu'au fichier ?

0 votes

C'est le cas, je vais modifier mon message pour le préciser. Je pense que la solution de lhunath suffira. Merci à tous pour votre aide !

982voto

lhunath Points 27045

Je suppose que vous voulez toujours voir STDERR et STDOUT sur le terminal. Vous pourriez opter pour la réponse de Josh Kelley, mais je trouve que le fait de garder une tail en arrière-plan, ce qui rend votre fichier journal très compliqué et maladroit. Remarquez que vous devez garder un FD exra et faire le nettoyage après coup en le tuant, et que techniquement, vous devriez le faire dans un fichier trap '...' EXIT .

Il y a une meilleure façon de faire, et vous l'avez déjà découverte : tee .

Seulement, au lieu de l'utiliser uniquement pour votre stdout, ayez un tee pour stdout et un pour stderr. Comment allez-vous procéder ? Substitution de processus et redirection de fichiers :

command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)

Divisons-nous et expliquons :

> >(..)

>(...) (substitution de processus) crée un FIFO et permet à tee écoutez-la. Ensuite, il utilise > (redirection de fichier) pour rediriger le STDOUT de command à la FIFO que votre premier tee est à l'écoute.

Même chose pour le second :

2> >(tee -a stderr.log >&2)

Nous utilisons à nouveau la substitution de processus pour faire un tee qui lit à partir de STDIN et le dépose dans le fichier stderr.log . tee renvoie son entrée sur STDOUT, mais comme son entrée est notre STDERR, nous voulons rediriger tee de la STDOUT de l'utilisateur vers notre STDERR. Ensuite, nous utilisons la redirection de fichiers pour rediriger command de STDERR vers l'entrée de la FIFO ( tee de STDIN).

Voir http://mywiki.wooledge.org/BashGuide/InputAndOutput

La substitution de processus est l'une de ces choses vraiment charmantes que vous obtenez en prime en choisissant bash comme interpréteur de commandes, par opposition à sh (POSIX ou Bourne).


Sur sh il faut faire les choses manuellement :

out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"

8 votes

J'ai essayé ça : $ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2) qui fonctionne, mais attend l'entrée. Y a-t-il une raison simple pour laquelle cela se produit ?

0 votes

@Justin : Je ne sais pas ce que vous voulez dire par "attend des données". La commande que vous avez donnée n'"attend" rien de moi.

0 votes

@lhunath, je n'utilise pas tcsh pour la programmation, mais merci pour la revue détaillée de ses défauts.

906voto

user542833 Points 1379

Tout simplement :

./aaa.sh 2>&1 | tee -a log

Cela redirige simplement l'erreur standard vers la sortie standard, donc tee renvoie les deux vers journal et à l'écran. Peut-être que quelque chose m'échappe, car certaines des autres solutions semblent vraiment compliquées.

Note : Depuis la version 4 de Bash, vous pouvez utiliser |& comme abréviation de 2>&1 | :

./aaa.sh |& tee -a log

123 votes

Cela fonctionne bien si vous voulez que stdout (canal 1) et stderr (canal 2) soient enregistrés dans le même fichier (un seul fichier contenant le mélange de stdout et de sterr). L'autre solution, plus compliquée, vous permet de séparer stdout et stderr en 2 fichiers différents (stdout.log et stderr.log, respectivement). Parfois, cela est important, parfois non.

29 votes

Les autres solutions sont bien plus compliquées que nécessaire dans de nombreux cas. Celle-ci fonctionne parfaitement pour moi.

23 votes

Le problème avec cette méthode est que vous perdez le code de sortie/état du processus aaa.sh, qui peut être important (par exemple, lors de l'utilisation dans un makefile). Vous n'avez pas ce problème avec la réponse acceptée.

82voto

Anthony Points 31

Cela peut être utile pour les personnes qui trouvent ce site via Google. Décommentez simplement l'exemple que vous voulez essayer. Bien sûr, n'hésitez pas à renommer les fichiers de sortie.

#!/bin/bash

STATUSFILE=x.out
LOGFILE=x.log

### All output to screen
### Do nothing, this is the default

### All Output to one file, nothing to the screen
#exec > ${LOGFILE} 2>&1

### All output to one file and all output to the screen
#exec > >(tee ${LOGFILE}) 2>&1

### All output to one file, STDOUT to the screen
#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)

### All output to one file, STDERR to the screen
### Note you need both of these lines for this to work
#exec 3>&1
#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)

### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen
#exec > ${STATUSFILE} 2>${LOGFILE}

### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen
#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)

### STDOUT to STATUSFILE and screen, STDERR to LOGFILE
#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}

### STDOUT to STATUSFILE, STDERR to LOGFILE and screen
#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)

echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}

9 votes

Non, et je pense que l'exécution a besoin d'explications. exec > signifie, déplacer la cible d'un descripteur de fichier vers une certaine destination. La valeur par défaut est 1, donc, exec > /dev/null déplace la sortie de stdout vers /dev/null à partir de maintenant dans cette session. Les descripteurs de fichiers actuels pour cette session peuvent être vus en faisant ls -l /dev/fd/ . Essayez-le ! Ensuite, voyez ce qui se passe lorsque vous émettez exec 2>/tmp/stderr.log. En outre, exec 3>&1 c'est-à-dire créer un nouveau descripteur de fichier avec le numéro 3, et le rediriger vers la cible du descripteur de fichier 1. Dans l'exemple, la cible était l'écran lorsque la commande a été lancée.

2 votes

L'exemple permettant d'afficher stdout et stderr à la fois à l'écran et dans des fichiers séparés est génial !!! Merci beaucoup !

1 votes

La partie "All output to one file, STDERR to the screen" peut être mise sur une seule ligne (il suffit de mettre 3>&1 à l'avant) : exec 3>&1 > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3) Notez que si vous voulez que stderr s'imprime à l'écran comme 2 encore, il suffit de passer à 3>&2 au lieu de 3>&1

23voto

Josh Kelley Points 24438

Pour rediriger stderr vers un fichier, afficher stdout à l'écran, et également enregistrer stdout dans un fichier :

./aaa.sh 2>ccc.out | tee ./bbb.out

EDIT : Pour afficher à la fois stderr et stdout à l'écran et également enregistrer les deux dans un fichier, vous pouvez utiliser la fonction de bash Redirection des E/S :

#!/bin/bash

# Create a new file descriptor 4, pointed at the file
# which will receive stderr.
exec 4<>ccc.out

# Also print the contents of this file to screen.
tail -f ccc.out &

# Run the command; tee stdout as normal, and send stderr
# to our file descriptor 4.
./aaa.sh 2>&4 | tee bbb.out

# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1

1 votes

Je suppose que l'utilisateur veut que stderr soit envoyé à sa console en plus du fichier, bien que cela n'ait pas été explicitement spécifié.

2 votes

J'aurais dû être plus clair, je voulais aussi que stderr soit affiché à l'écran. J'ai toujours apprécié la solution de Josh Kelley, mais je trouve que celle de lhunath répond mieux à mes besoins. Merci à tous !

23voto

Gilles Points 37537

En d'autres termes, vous voulez envoyer stdout dans un filtre ( tee bbb.out ) et stderr dans un autre filtre ( tee ccc.out ). Il n'y a pas de moyen standard de canaliser autre chose que stdout dans une autre commande, mais vous pouvez contourner cela en jonglant avec les descripteurs de fichiers.

{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2

Voir aussi Comment grep le flux d'erreur standard (stderr) ? y Quand utiliseriez-vous un descripteur de fichier supplémentaire ?

Dans bash (et ksh et zsh), mais pas dans les autres shells POSIX tels que dash, vous pouvez utiliser substitution de processus :

./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)

Attention, dans bash, cette commande revient dès que ./aaa.sh se termine, même si le tee sont toujours exécutées (ksh et zsh attendent les sous-processus). Cela peut être un problème si vous faites quelque chose comme ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out . Dans ce cas, utilisez la jonglerie des descripteurs de fichiers ou ksh/zsh à la place.

4 votes

Cela semble être la seule solution qui permette de conserver les flux stdout/stderr tels quels (c'est-à-dire sans les fusionner). Super !

0 votes

L'approche la plus simple pour sh utile pour les tâches cron, où la substitution de processus n'est pas disponible.

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