web-dev-qa-db-fra.com

Comment écrire stderr dans un fichier en utilisant "tee" avec un tuyau?

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

./aaa.sh | tee bbb.out

Comment pourrais-je aussi écrire STDERR dans un fichier nommé ccc.out, tout en le faisant toujours afficher?

492
jparanich

Je suppose que vous voulez toujours voir STDERR et STDOUT sur le terminal. Vous pourriez chercher la réponse de Josh Kelley, mais je trouve que garder un tail à l'arrière-plan qui génère votre fichier journal est très rudimentaire et compliqué. Remarquez comment vous devez garder un exra FD et nettoyer ensuite en le tuant. Techniquement, vous devriez le faire dans un trap '...' EXIT.

Il existe un meilleur moyen de le faire et vous l'avez déjà découvert: tee.

Seulement, au lieu de l’utiliser pour votre stdout, prenez un tee pour stdout et un pour stderr. Comment allez-vous accomplir cela? Substitution de processus et redirection de fichier:

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

Séparons-le et expliquons:

> >(..)

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

Même chose pour le second:

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

Nous utilisons à nouveau la substitution de processus pour créer un processus tee qui lit à partir de STDIN et le dépose dans stderr.log. tee renvoie son entrée sur STDOUT, mais cette entrée étant notre STDERR, nous souhaitons rediriger le STDOUT de tee vers notre STDERR. Ensuite, nous utilisons la redirection de fichier pour rediriger STDERR de command vers l'entrée FIFO (STDIN de tee.

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

La substitution de processus est une de ces choses vraiment agréables que vous obtenez en prime en choisissant bash comme Shell, par opposition à sh (POSIX ou Bourne).


Dans sh, vous devez 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"
725
lhunath

pourquoi pas simplement:

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

Ceci redirige simplement stderr vers stdout. Le tee renvoie donc à la fois au journal et à l'écran. Peut-être me manque quelque chose, parce que 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
621
user542833

Cela peut être utile pour les personnes qui trouvent cela 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}
54
Anthony

Pour rediriger stderr vers un fichier, affichez stdout sur écran et enregistrez également stdout dans un fichier:

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

EDIT: pour afficher à la fois stderr et stdout à l'écran et également les sauvegarder dans un fichier, vous pouvez utiliser le bash redirection d'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
21
Josh Kelley

En d'autres termes, vous souhaitez diriger stdout dans un filtre (tee bbb.out) et stderr dans un autre filtre (tee ccc.out). Il n'y a pas de méthode standard pour diriger n'importe quoi autre que stdout dans une autre commande, mais vous pouvez contourner ce problème en jonglant avec les descripteurs de fichier.

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

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

Dans bash (et ksh et zsh), mais pas dans d'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 retourne dès que ./aaa.sh est terminé, même si les commandes tee sont toujours exécutées (ksh et zsh attendent les sous-processus). Cela peut poser 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 plutôt le jongleur de descripteur de fichier ou ksh/zsh.

16
Gilles

Si vous utilisez bash:

# Redirect standard out and standard error separately
% cmd >stdout-redirect 2>stderr-redirect

# Redirect standard error and out together
% cmd >stdout-redirect 2>&1

# Merge standard error with standard out and pipe
% cmd 2>&1 |cmd2

Le crédit (sans répondre du haut de ma tête) va ici: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html

13
ChristopheD

Ce qui suit fonctionnera pour KornShell (ksh) où la substitution de processus n’est pas disponible,

# create a combined(stdin and stdout) collector
exec 3 <> combined.log

# stream stderr instead of stdout to tee, while draining all stdout to the collector
./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3

# cleanup collector
exec 3>&-

Le vrai truc ici est la séquence du 2>&1 1>&3 qui, dans notre cas, redirige la stderr vers stdout et redirige la stdout vers le descripteur 3. A ce stade, les stderr et stdout ne sont pas encore combinés.

En effet, la stderr (comme stdin) est transmise à tee où elle se connecte à stderr.log et redirige également vers le descripteur 3.

Et le descripteur 3 le connecte à combined.log tout le temps. Ainsi, le combined.log contient à la fois stdout et stderr.

3
shuva

Dans mon cas, un script exécutait une commande en redirigeant stdout et stderr vers un fichier, quelque chose comme:

cmd > log 2>&1

Je devais le mettre à jour de manière à ce que, en cas de panne, agisse en fonction des messages d'erreur. Je pourrais bien sûr supprimer le dup 2>&1 et capturer le script stderr, mais les messages d'erreur n'entreront pas dans le fichier journal à titre de référence. Alors que la réponse acceptée de @lhunath est censée faire la même chose, elle redirige stdout et stderr vers différents fichiers, ce qui n'est pas ce que je veux, mais cela m'a aidé à trouver la solution exacte J'ai besoin:

(cmd 2> >(tee /dev/stderr)) > log

Avec ce qui précède, log aura une copie de stdout et stderr et je pourrai capturer stderr de mon script sans avoir à vous soucier de stdout.

3
haridsv