781 votes

Rediriger stderr et stdout dans Bash

Je veux rediriger à la fois stdout et stderr d'un processus vers un seul fichier. Comment puis-je faire cela dans Bash ?

2 votes

J'aimerais dire que cette question est étonnamment utile. Beaucoup de gens ne savent pas comment faire, car ils n'ont pas à le faire fréquemment, et ce n'est pas le comportement le mieux documenté de Bash.

3 votes

Parfois, il est utile de voir la sortie (comme d'habitude) ET de la rediriger vers un fichier. Voir la réponse de Marko ci-dessous. (Je le dis ici car il est facile de se contenter de la première réponse acceptée si cela suffit à résoudre un problème, mais les autres réponses fournissent souvent des informations utiles).

867voto

dirkgently Points 56879

Jetez un coup d'œil aquí . Devrait être :

yourcommand &>filename

(qui redirige les deux stdout y stderr au nom de fichier).

35 votes

Cette syntaxe est dépréciée selon les règles de l'UE. Bash Hackers Wiki . C'est ça ?

21 votes

Según wiki.bash-hackers.org/scripting/obsolete Il semble qu'il soit obsolète dans le sens où il ne fait pas partie de POSIX, mais la page de manuel de bash ne mentionne pas qu'il sera supprimé de bash dans un futur proche. La page de manuel indique une préférence pour '&>' par rapport à '>&', qui est par ailleurs équivalent.

13 votes

Je suppose que nous ne devrions pas utiliser &> car il n'est pas dans POSIX, et les shells courants tels que "dash" ne le supportent pas.

511voto

Marko Points 13736
do_something 2>&1 | tee -a some_file

Cela va rediriger stderr vers stdout et stdout vers some_file et l'imprimer sur stdout.

17 votes

Sous AIX (ksh), votre solution fonctionne. La réponse acceptée do_something &>filename ne le fait pas. +1.

12 votes

@Daniel, mais cette question concerne spécifiquement bash

3 votes

Je reçois Ambiguous output redirect. Vous savez pourquoi ?

291voto

f3lix Points 13634

Vous pouvez rediriger stderr a stdout y el stdout dans un fichier :

some_command >file.log 2>&1 

Ver http://tldp.org/LDP/abs/html/io-redirection.html

Ce format est préféré au format le plus populaire &> qui ne fonctionne que dans bash. Dans le shell Bourne, il pourrait être interprété comme l'exécution de la commande en arrière-plan. De plus, le format est plus lisible 2 (est STDERR) redirigé vers 1 (STDOUT).

EDIT : changement de l'ordre comme indiqué dans les commentaires

1 votes

Quel est l'avantage de cette approche par rapport à some_command &> file.log ?

7 votes

Si vous voulez ajouter à un fichier, vous devez le faire de cette façon : echo "foo" 2>&1 1>> bar.txt AFAIK il n'y a aucun moyen d'ajouter en utilisant &>.

12 votes

Argh, désolé, echo "foo" 1>> bar.txt 2>&1

233voto

quizac Points 201
# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-

# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE

# Redirect STDERR to STDOUT
exec 2>&1

echo "This line will appear in $LOG_FILE, not 'on screen'"

Maintenant, un simple écho écrira dans $LOG_FILE. Utile pour la démonisation.

À l'auteur du message original,

Cela dépend de ce que vous voulez réaliser. Si vous avez juste besoin de rediriger les entrées/sorties d'une commande que vous appelez depuis votre script, les réponses sont déjà données. La mienne consiste à rediriger sur le script actuel qui affecte toutes les commandes/built-ins (y compris les forks) après l'extrait de code mentionné.


Une autre solution intéressante consiste à rediriger à la fois vers std-err/out ET vers le logger ou le fichier de log en même temps, ce qui implique de diviser "un flux" en deux. Cette fonctionnalité est fournie par la commande 'tee' qui peut écrire/appliquer sur plusieurs descripteurs de fichiers (fichiers, sockets, pipes, etc.) en une seule fois : tee FILE1 FILE2 ... >(cmd1) >(cmd2) ...

exec 3>&1 4>&2 1> >(tee >(logger -i -t 'my_script_tag') >&3) 2> >(tee >(logger -i -t 'my_script_tag') >&4)
trap 'cleanup' INT QUIT TERM EXIT

get_pids_of_ppid() {
    local ppid="$1"

    RETVAL=''
    local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
    RETVAL="$pids"
}

# Needed to kill processes running in background
cleanup() {
    local current_pid element
    local pids=( "$$" )

    running_pids=("${pids[@]}")

    while :; do
        current_pid="${running_pids[0]}"
        [ -z "$current_pid" ] && break

        running_pids=("${running_pids[@]:1}")
        get_pids_of_ppid $current_pid
        local new_pids="$RETVAL"
        [ -z "$new_pids" ] && continue

        for element in $new_pids; do
            running_pids+=("$element")
            pids=("$element" "${pids[@]}")
        done
    done

    kill ${pids[@]} 2>/dev/null
}

Donc, depuis le début. Supposons que nous ayons un terminal connecté à /dev/stdout(FD #1) et /dev/stderr(FD #2). En pratique, il peut s'agir d'un tuyau, d'un socket ou autre.

  • Créez les FDs #3 et #4 et pointez vers le même "emplacement" que #1 et #2 respectivement. Changer le FD #1 n'affecte pas le FD #3 à partir de maintenant. Maintenant, les FDs #3 et #4 pointent vers STDOUT et STDERR respectivement. Ils seront utilisés comme réel les terminaux STDOUT et STDERR.
  • 1> >(...) redirige STDOUT vers la commande entre parenthèses
  • parens(sub-shell) exécute 'tee' à partir du STDOUT(pipe) de exec et redirige la commande 'logger' via un autre pipe vers le sub-shell de parens. En même temps, il copie la même entrée vers FD #3(terminal).
  • la deuxième partie, très similaire, consiste à faire la même chose pour STDERR et FDs #2 et #4.

Le résultat de l'exécution d'un script ayant la ligne ci-dessus et en plus celle-ci :

echo "Will end up in STDOUT(terminal) and /var/log/messages"

...est le suivant :

$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages

$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(terminal) and /var/log/messages

Si vous voulez voir une image plus claire, ajoutez ces 2 lignes au script :

ls -l /proc/self/fd/
ps xf

1 votes

Une seule exception. dans le premier exemple, vous avez écrit : exec 1<>$LOG_FILE . il en résulte que le fichier journal original est toujours écrasé. pour un vrai journal, la meilleure façon est : exec 1>>$LOG_FILE il en résulte que le journal est toujours ajouté.

5 votes

C'est vrai, mais cela dépend des intentions. Mon approche consiste à toujours créer un fichier journal unique et horodaté. L'autre approche consiste à ajouter des fichiers. Les deux méthodes sont "logrotables". Je préfère les fichiers séparés qui nécessitent moins d'analyse, mais comme je l'ai dit, tout ce qui fait flotter votre bateau :)

0 votes

Comment repasser du fichier journal à la sortie standard ?

46voto

Guðmundur H Points 3323
bash your_script.sh 1>file.log 2>&1

1>file.log indique au shell d'envoyer STDOUT au fichier file.log et 2>&1 lui indique de rediriger STDERR (descripteur de fichier 2) vers STDOUT (descripteur de fichier 1).

Nota: L'ordre importe comme l'a souligné liw.fi, 2>&1 1>file.log ne fonctionne pas.

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