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?
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"
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
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}
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
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.
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
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
.
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
.