Copiez stdout et stderr dans un fichier journal et laissez-les sur la console dans le script lui-même

11

En utilisant bash, comment copier stderr et stdout dans un fichier journal et les laisser également affichés sur la console?

Je voudrais le faire dans le script lui-même en utilisant un exec.

J'ai essayé avec

exec &>> log.out

echo "This is stdout"
echo "This is stderr" >&2

Mais ce qui précède n'imprime rien sur la console. Comment puis-je y parvenir en bash?

adarshr
la source
Il existe une réponse très positive à une question similaire sur StackOverflow qui répond à cette question de manière assez approfondie. stackoverflow.com/a/692407/208257
Dan Burton

Réponses:

10

Vous recherchez tee.

Voir man teepour plus de détails.

Pour le combiner avec exec, vous devez utiliser la substitution de processus . (Voir man bashpour plus de détails.)

exec &> >(tee  log.out)
echo "This is stdout"
echo "This is stderr" >&2
H.-Dirk Schmitt
la source
Je l'ai regardé. Faire exec 2>&1 | tee -a log.outuniquement des impressions sur la console, rien dans le fichier journal. Peut-être que je n'utilise pas la bonne syntaxe?
adarshr
@adarshr Veuillez revérifier votre code. J'ai écrit sur teeen combinaison avec la substitution de processus .
H.-Dirk Schmitt
1
Cela fusionne cependant stdout et stderr et peut avoir le même type d'effet secondaire que celui mentionné dans ma réponse.
Stéphane Chazelas
@StephaneChazelas Regardez l'exemple de code dans la question - c'était l'intention du PO.
H.-Dirk Schmitt
Ce que je veux dire par fusion, c'est que maintenant les erreurs du script vont à stdout. Donc, si quelqu'un le fait the-script | wc -lpar exemple, cela comptera également les lignes d'erreur et vous ne verrez pas les erreurs.
Stéphane Chazelas
5

Tu peux faire:

: > log # empty log file if necessary
{ { {

  ...the script

} 3>&- | tee -a log >&3 3>&-
exit "${PIPESTATUS[0]}"
} 2>&1 | tee -a log >&2 3>&-
} 3>&1
exit "${PIPESTATUS[0]}"

Vous pouvez également l'écrire comme suit:

: > log # empty log file if necessary
exec 2> >(tee -a log >&2) > >(tee -a log)

...the script

Mais parce que bash n'attend pas les processus commencés par >(...), cela a parfois pour effet de sortir quelque chose vers le terminal après le retour de la commande, ce qui peut avoir des effets encore plus désagréables (comme rejeter silencieusement cette sortie) si l'attribut "tostop" du terminal est sur.

Dans tous les cas, en créant stdoutun canal dans les deux solutions, et parce que deux commandes produisent indépendamment les messages de sortie et d'erreur, cela affectera la mise en mémoire tampon de sortie et l'ordre d'affichage des messages de sortie et d'erreur.

Stéphane Chazelas
la source
Je pense que votre deuxième tee manque une redirection vers stderr. Il devrait être:tee -a log >&2 3>&-
richvdh
5

Je sais que c'est un vieux post, mais pourquoi ne pas faire ça?

echo "hi" >> log.txt #stdout -> log
echo "hi" | tee -a log.txt #stdout -> log & stdout
echo "hi" &>> log.txt #stdout & stderr -> log
echo "hi" |& tee -a log.txt #stdout & stderr -> log & stdout

Et bien sûr, si vous voulez une sortie standard, vous pouvez simplement imprimer régulièrement.

Vous pouvez le faire avec n'importe quelle combinaison de flux que vous souhaitez, en utilisant simplement ces deux commandes de base.

Je sais que je suis venu ici et que je n'ai pas obtenu de réponse facile à comprendre / à mettre en œuvre, j'espère que cela aidera quelqu'un d'autre qui est en difficulté.

Soit dit en passant, pour les noobs là-bas comme mon moi précédent, tout ce que la teecommande fait est de sortir l'entrée stdin à la fois sur stdout et le ou les fichiers spécifiés comme arguments ultérieurs. -asignifie append, donc vous n'écrasez pas le fichier à chaque utilisation de la commande. Si vous avez d'autres questions, je trouve que c'est une ressource très utile pour apprendre rapidement bash.

user2624583
la source
1
Merci d'avoir contribué. D'un autre côté, j'ai totalement oublié ce que j'essayais de faire :-)
adarshr
1
+1 pour donner des exemples de tee multiples, agréables et simples.
Tim
2

Une autre façon de procéder consiste à utiliser des redirections dans les fonctions.

#!/bin/bash

function1 () {
    echo 'STDOUT from function 1'
    echo 'STDERR from function 1' >&2
}

function2 () {
    echo 'STDOUT from function 2'
    echo 'STDERR from function 2' >&2
}


function3 () {
    echo 'STDOUT from function 3'
    echo 'STDERR from function 3' >&2
}

main() {
    function1
    function2
    function3
}

main 2>&1 |tee log.txt

Ici, nous avons une mainfonction qui appelle toutes les autres fonctions. Maintenant , la réorientation STDOUTet STDERRde la mainfonction tee.

Kannan Mohan
la source
À mon humble avis, c'est la façon la plus propre de le faire, au moins pour les cas simples. Merci.
ACK_stoverflow