Question Rediriger stderr et stdout dans Bash


Je veux rediriger à la fois stdout et stderr d'un processus vers un seul fichier. Comment est-ce que je fais ça dans Bash?


564
2018-03-12 09:14


origine


Réponses:


Regarde ici. Devrait être:

yourcommand &>filename

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


661
2018-03-12 09:17



do_something 2>&1 | tee -a some_file

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


393
2018-03-12 09:16



Vous pouvez rediriger stderr à stdout et le stdout dans un fichier:

some_command >file.log 2>&1 

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

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

EDIT: changé l'ordre comme indiqué dans les commentaires


201
2017-12-13 10:30



# 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 echo écrira dans $ LOG_FILE. Utile pour démoniser.

Pour l'auteur du message original,

Cela dépend de ce que vous devez accomplir. Si vous avez juste besoin de rediriger dans / hors d'une commande que vous appelez de votre script, les réponses sont déjà données. Le mien est sur la redirection dans script courant qui affecte toutes les commandes / built-ins (inclut les fourches) après l'extrait de code mentionné.


Une autre solution intéressante consiste à rediriger vers std-err / out ET vers un enregistreur ou un fichier journal à la fois, ce qui implique de diviser "un flux" en deux. Cette fonctionnalité est fournie par la commande 'tee' qui peut écrire / ajouter à plusieurs descripteurs de fichiers (fichiers, sockets, pipes, etc) à la 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, ce pourrait être un tuyau, une douille ou autre chose.

  • Créez les FD # 3 et # 4 et pointez sur le même "emplacement" que # 1 et # 2 respectivement. Changer FD # 1 n'affecte plus le FD # 3 à partir de maintenant. Maintenant, FDs # 3 et # 4 pointent vers STDOUT et STDERR respectivement. Ceux-ci seront utilisés comme réal terminal STDOUT et STDERR.
  • 1>> (...) redirige STDOUT pour commander en parens
  • parens (sous-shell) exécute la lecture de «tee» à partir de STDOUT (pipe) d'exec et redirige vers la commande «logger» via un autre pipe à sous-shell en parens. En même temps, il copie la même entrée à FD # 3 (terminal)
  • la deuxième partie, très similaire, consiste à faire le même tour 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 celui-ci:

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

...est comme suit:

$ ./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

171
2018-03-12 09:17



bash your_script.sh 1>file.log 2>&1

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

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


34
2018-05-06 14:12



Curieusement, cela fonctionne:

yourcommand &> filename

Mais cela donne une erreur de syntaxe:

yourcommand &>> filename
syntax error near unexpected token `>'

Vous devez utiliser:

yourcommand 1>> filename 2>&1

20
2017-11-19 01:55



Réponse courte: Command >filename 2>&1 ou Command &>filename


Explication:

Considérez le code suivant qui imprime le mot "stdout" à stdout et le mot "stderror" à stderror.

$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror

Notez que l'opérateur '&' indique à bash que 2 est un descripteur de fichier (qui pointe vers le fichier stderr) et non un nom de fichier. Si nous avons omis le '&', cette commande imprime stdout à stdout, et créer un fichier nommé "2" et écrire stderror Là.

En expérimentant avec le code ci-dessus, vous pouvez voir par vous-même comment fonctionnent les opérateurs de redirection. Par exemple, en changeant quel fichier parmi les deux descripteurs 1,2, est redirigé vers /dev/null les deux lignes de code suivantes suppriment tout de la stdout, et tout de stderror respectivement (impression ce qui reste).

$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout

Maintenant, nous pouvons expliquer pourquoi la solution pourquoi le code suivant ne produit pas de sortie:

(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1

Pour vraiment comprendre cela, je vous recommande fortement de lire ceci page Web sur les tables des descripteurs de fichiers. En supposant que vous ayez fait cette lecture, nous pouvons continuer. Notez que les processus Bash sont de gauche à droite. Bash voit donc >/dev/null premier (qui est le même que 1>/dev/null), et définit le descripteur de fichier 1 pour pointer vers / dev / null au lieu de stdout. Après avoir fait cela, Bash se déplace ensuite vers la droite et voit 2>&1. Ceci définit le descripteur de fichier 2 pour pointer vers le même fichier en tant que descripteur de fichier 1 (et non le fichier descripteur 1 lui-même !!!! cette ressource sur les pointeurs pour plus d'informations)) . Comme le descripteur de fichier 1 pointe vers / dev / null et que le descripteur de fichier 2 pointe vers le même fichier que le descripteur de fichier 1, le descripteur de fichier 2 pointe également vers / dev / null. Ainsi, les deux descripteurs de fichiers pointent vers / dev / null, et c'est pourquoi aucune sortie n'est rendue.


Pour tester si vous comprenez vraiment le concept, essayez de deviner la sortie lorsque nous changeons l'ordre de redirection:

(echo "stdout"; echo "stderror" >&2)  2>&1 >/dev/null

 stderror

Le raisonnement ici est qu'évaluant de gauche à droite, Bash voit 2> & 1, et place ainsi le descripteur de fichier 2 pour pointer au même endroit que le descripteur de fichier 1, c'est-à-dire stdout. Il définit ensuite le descripteur de fichier 1 (rappelez-vous que> / dev / null = 1> / dev / null) pour pointer vers> / dev / null, supprimant ainsi tout ce qui serait normalement envoyé à la sortie standard. Ainsi tout ce qui nous reste est ce qui n'a pas été envoyé à stdout dans le sous-shell (le code entre parenthèses) - c'est-à-dire "stderror".   La chose intéressante à noter est que, même si 1 est juste un pointeur vers la sortie standard, rediriger le pointeur 2 vers 1 via 2>&1 ne forme PAS une chaîne de pointeurs 2 -> 1 -> stdout. Si c'est le cas, suite à la redirection de 1 vers / dev / null, le code 2>&1 >/dev/null donnerait la chaîne de pointeur 2 -> 1 -> / dev / null, et donc le code ne générerait rien, contrairement à ce que nous avons vu plus haut.


Enfin, je note qu'il y a une façon plus simple de faire ceci:

De la section 3.6.4 ici, on voit qu'on peut utiliser l'opérateur &> rediriger à la fois stdout et stderr. Ainsi, pour rediriger les sorties stderr et stdout de n'importe quelle commande vers \dev\null (qui supprime la sortie), nous tapons simplement $ command &> /dev/null  ou dans le cas de mon exemple:

$ (echo "stdout"; echo "stderror" >&2) &>/dev/null

Principaux points à retenir:

  • Les descripteurs de fichiers se comportent comme des pointeurs (bien que les descripteurs de fichiers ne soient pas les mêmes que les pointeurs de fichiers)
  • Rediriger un descripteur de fichier "a" vers un descripteur de fichier "b" qui pointe vers le fichier "f" fait que le descripteur de fichier "a" pointe vers le même emplacement que le fichier descripteur b "f". Il ne forme pas une chaîne de pointeurs a -> b -> f
  • En raison de ce qui précède, l'ordre compte, 2>&1 >/dev/null est! = >/dev/null 2>&1. L'un génère la sortie et l'autre non!

Enfin, jetez un oeil à ces excellentes ressources:

Documentation Bash sur la redirection, Une explication des tables de descripteur de fichier, Introduction aux pointeurs


9
2018-04-23 13:14



LOG_FACILITY="local7.notice"
LOG_TOPIC="my-prog-name"
LOG_TOPIC_OUT="$LOG_TOPIC-out[$$]"
LOG_TOPIC_ERR="$LOG_TOPIC-err[$$]"

exec 3>&1 > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" )
exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" )

C'est lié: écrire stdOut & stderr à syslog.

Il fonctionne presque, mais pas de xinted;


8
2018-01-19 19:21



Je voulais une solution pour que la sortie de stdout plus stderr soit écrite dans un fichier journal et stderr toujours sur la console. Donc j'avais besoin de dupliquer la sortie stderr via tee.

C'est la solution que j'ai trouvée:

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
  • Premier échange stderr et stdout
  • puis ajoutez la sortie standard au fichier journal
  • tuyau stderr à tee et l'ajouter également au fichier journal

5
2018-02-04 13:57