web-dev-qa-db-fra.com

Comment rediriger stdout vers un fichier, et stdout + stderr vers un autre?

Comment réaliser

cmd >> file1 2>&1 1>>file2

Autrement dit, le stdout et stderr doit rediriger vers un fichier (fichier1) et seul stdout (fichier2) doit rediriger vers un autre (les deux en mode ajout)?

33
Swarna Gowri

Le problème est que lorsque vous redirigez votre sortie, elle n'est plus disponible pour la prochaine redirection. Vous pouvez diriger vers tee dans un sous-shell pour conserver la sortie pour la deuxième redirection:

( cmd | tee -a file2 ) >> file1 2>&1

ou si vous souhaitez voir la sortie dans le terminal:

( cmd | tee -a file2 ) 2>&1 | tee -a file1

Pour éviter d'ajouter le stderr du premier tee à file1, vous devez rediriger le stderr de votre commande vers un descripteur de fichier (par exemple 3), puis l'ajouter à nouveau à stdout:

( 2>&3 cmd | tee -a file2 ) >> file1 3>&1
# or
( 2>&3 cmd | tee -a file2 ) 3>&1 | tee -a file1

(merci @ fra-san)

42
pLumo

Avec zsh:

cmd >& out+err.log > out.log

En mode ajout:

cmd >>& out+err.log >> out.log

Dans zsh, et à condition que l'option mult_ios N'ait pas été désactivée, lorsqu'un descripteur de fichier (ici 1) est redirigé plusieurs fois pour l'écriture, le Shell implémente un tee pour dupliquer la sortie sur toutes les cibles.

16

Vous pouvez: tag stdout (en utilisant un sed UNBUFFERED, ie: sed -u ...), ont également stderr aller à stdout (non balisé, car il n'a pas traversé cette balise sed), et ainsi être en mesure de différencier les 2 dans le fichier journal résultant.

Ce qui suit: is slow (Il peut être sérieusement optimisé, en utilisant par exemple un script Perl au lieu de while ...; do ...; done, par exemple, qui générera des sous-coquilles et des commandes à chaque ligne!), bizarre (il semble que j'aie besoin des 2 {} étapes pour renommer stdout puis dans l'autre ajouter le stderr "tombé"), etc. Mais est: "proof of concept", qui essaiera de garder le plus possible l'ordre de sortie de stdout & stderr:

#basic principle (some un-necessary "{}" to visually help see the layers):
# { { complex command ;} | sed -e "s/^/TAGstdout/" ;} 2>&1 | read_stdin_and_redispatch

#exemple:
# complex command = a (slowed) ls of several things (some existing, others not)
#  to see if the order of stdout&stderr is kept

#preparation, not needed for the "proof of concept", but needed for our specific exemple setup:
\rm out.file out_AND_err.file unknown unknown2 
touch existing existing2 existing3

#and the (slow, too many execs, etc) "proof of concept":
uniquetag="_stdout_" # change this to something unique, that will NOT appear in all the commands outputs... 
                     # avoid regexp characters ("+" "?" "*" etc) to make it easy to remove with another sed later on.

{
   { for f in existing unknown existing2 unknown2 existing3 ; do ls -l "$f" ; sleep 1; done ;
   } | sed -u -e "s/^/${uniquetag}/" ;
} 2>&1 | while IFS="" read -r line ; do
    case "$line" in
       ${uniquetag}*) printf "%s\n" "$line" | tee -a out_AND_err.file | sed -e "s/^${uniquetag}//" >> out.file ;; 
        *)            printf "%s\n" "$line"       >> out_AND_err.file ;;   
    esac; 
done;

# see the results:
grep "^" out.file out_AND_err.file
3
Olivier Dulac

Si l'ordre de sortie doit être: stdout alors stderr ; il n'y a pas de solution avec redirection uniquement.
Le stderr doit être stocké dans un fichier temporel

cmd 2>>file-err | tee -a file1 >>file2
cat file-err >> file1
rm file-err

La description:

La seule façon de rediriger la sortie one (un fd comme stdout ou stderr) vers les fichiers two est de la reproduire. La commande tee est l'outil approprié pour reproduire le contenu d'un descripteur de fichier. Ainsi, une première idée d'avoir une sortie sur deux fichiers serait d'utiliser:

... |  tee file1 file2

Cela reproduit le stdin de tee dans les deux fichiers (1 et 2), laissant la sortie de tee toujours inutilisée. Mais nous devons ajouter (utiliser -a) Et n'avoir besoin que d'une copie. Cela résout les deux problèmes:

... | tee -a file1 >>file2

Pour fournir à tee avec stdout (celui à répéter), nous devons consommer stderr directement hors de la commande. D'une manière, si l'ordre n'est pas important (l'ordre de sortie sera (très probablement) préservé tel qu'il est généré, celui qui est sorti en premier sera stocké en premier). Soit:

  1. cmd 2>>file1 | tee -a file2 >>file1
  2. cmd 2>>file1 > >( tee -a file2 >>file1 )
  3. ( cmd | tee -a file2 ) >> file1 2>&1

L'option 2 ne fonctionne que dans certains shells. L'option 3 utilise un sous-shell supplémentaire (plus lent) mais n'utilise les noms de fichier qu'une seule fois.

Mais si stdout must être le premier (quelle que soit la sortie de l'ordre générée), nous devons stocker stderr pour l'ajouter au fichier à la fin (première solution publiée).

2
Isaac

Dans l'intérêt de la diversité:

Si votre système prend en charge /dev/stderr, puis

(cmd | tee -a /dev/stderr) 2>> file1 >> file2

marchera. La sortie standard du cmd est envoyée à la fois au stdout et au stderr du pipeline. L'erreur standard du cmd contourne le tee et sort le stderr du pipeline.

Donc

  • la sortie standard du pipeline est simplement la sortie standard du cmd, et
  • le stderr du pipeline est le stdout et le stderr du cmd, mélangés.

Il suffit ensuite d'envoyer ces flux vers les fichiers appropriés.

Comme avec presque toutes les approches comme celle-ci (y compris réponse de Stéphane ), file1 peut mettre les lignes hors service.

1
Scott