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)?
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)
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.
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
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:
cmd 2>>file1 | tee -a file2 >>file1
cmd 2>>file1 > >( tee -a file2 >>file1 )
( 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).
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
cmd
, etcmd
, 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.