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


J'ai en dessous une commande qui va imprimer la sortie de aaa.sh à l'écran tout en écrivant stdout à bbb.out; mais je voudrais aussi écrire stderr à un fichier nommé ccc.out. Des suggestions sur la façon de modifier la pièce ci-dessous?

./aaa.sh | tee ./bbb.out

Mettre à jour:  stdout et stderr devraient toujours être tous deux imprimés à l'écran, peu importe.


412
2018-03-28 01:53


origine


Réponses:


Je suppose que vous voulez toujours voir STDERR et STDOUT sur le terminal. Vous pouvez opter pour la réponse de Josh Kelley, mais je trouve que tail autour de l'arrière-plan qui génère votre fichier journal très hackish et cludgy. Notez comment vous devez garder une exra FD et faire le nettoyage après en la tuant et que techniquement cela devrait être fait dans un trap '...' EXIT.

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

Seulement, au lieu de simplement l'utiliser pour votre sortie standard, ayez 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)

Divisons-le et expliquons:

> >(..)

>(...) (substitution de processus) crée un FIFO et permet tee écoute dessus. Ensuite, il utilise > (redirection de fichier) pour rediriger le STDOUT de command au FIFO que votre premier tee écoute sur.

Même chose pour la seconde:

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

Nous utilisons à nouveau la substitution de processus pour faire un tee processus qui lit à partir de STDIN et le déverse dans stderr.log. tee retourne son entrée sur STDOUT, mais comme son entrée est notre STDERR, nous voulons rediriger teeSTDOUT à notre STDERR à nouveau. Ensuite, nous utilisons la redirection de fichiers pour rediriger commandSTDERR à l'entrée du FIFO (teede STDIN).

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

La substitution de processus est l'une de ces choses vraiment belles que vous obtenez en bonus de choisir bash comme votre coquille 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"

615
2018-03-28 07:54



pourquoi pas simplement:

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

Cela redirige simplement stderr à stdout, donc tee fait écho à la fois pour se connecter et à l'écran. Peut-être qu'il me manque quelque chose, parce que certaines des autres solutions semblent vraiment compliquées.

Remarque: Depuis bash version 4, vous pouvez utiliser |& comme abréviation pour 2>&1 |:

./aaa.sh |& tee -a log

507
2017-08-09 05:02



Cela peut être utile pour les personnes qui trouvent cela via google. Décommentez simplement l'exemple que vous souhaitez 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}

42
2017-10-09 18:21



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

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

MODIFIER: Pour afficher à la fois stderr et stdout à l'écran et enregistrer les deux dans un fichier, vous pouvez utiliser 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

19
2018-03-28 01:57



En d'autres termes, vous voulez rediriger 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 transférer autre chose que stdout dans une autre commande, mais vous pouvez contourner ce problème en jonglant avec les descripteurs de fichiers.

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

Voir également Comment faire pour grep le 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 tiret, 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 finit, même si le tee les commandes 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 plutôt la jonglerie de descripteur de fichier ou ksh / zsh.


13
2018-02-06 19:24



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 (ne répondant pas du haut de ma tête) va ici: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html


11
2018-03-28 02:00



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

cmd > log 2>&1

Je devais le mettre à jour de telle sorte que, en cas de panne, effectuez des actions en fonction des messages d'erreur. Je pourrais bien sûr enlever le dup 2>&1 et capturer le stderr à partir du script, mais les messages d'erreur ne seront pas dans le fichier journal pour référence. Alors que la réponse acceptée de @lhunath est supposée faire la même chose, elle redirige stdout et stderr à différents fichiers, ce qui n'est pas ce que je veux, mais cela m'a aidé à trouver la solution exacte dont j'ai besoin:

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

Avec ce qui précède, le journal aura une copie des deux stdout et stderr et je peux capturer stderr de mon script sans avoir à se soucier de stdout.


2
2017-07-31 23:01



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 tour ici, est la séquence de la 2>&1 1>&3 qui dans notre cas redirige le stderr à stdout et redirige le stdout au descripteur 3. À ce stade, le stderr et stdout ne sont pas encore combinés.

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

Et descripteur 3 est la connexion à combined.log tout le temps. Alors le combined.log contient à la fois stdout et stderr.


2