Comment rediriger stdout et stderr vers un fichier et afficher stderr vers la console?

18

Je sais comment rediriger vers un fichier et utiliser tee; au niveau de base. Donc

$ alias outanderr='bash -c "echo stdout >&1; echo stderr >&2"'
# A fake "application" displaying both output and error messages.

$ outanderr 1>file      # redirect stdout to a file, display stderr
stderr

$ outanderr 2>file      # redirect stderr to a file, display stdout
stdout

$ outanderr 1>file 2>&1 # redirect both to a file, display nothing

$ outanderr | tee file; echo "-- file contents --" && cat file
# redirect stdout to a file, display both (note: order is messed up)
stderr
stdout
-- file contents --
stdout

$ outanderr 2>&1 | tee file; echo "-- file contents --" && cat file
# redirect both to a file, display both
stdout
stderr
-- file contents --
stdout
stderr

La question est: quoi écrire à la place des points d'interrogation pour obtenir la sortie ci-dessous:

$ outanderr ???; echo "-- file contents --" && cat file
# redirect both to a file, display stderr
stderr
-- file contents --
stdout
stderr

Constaints:

  • En supposant que bash.
  • La commande doit être conservée dans le dossier.
  • le contenu de stderr est affiché en temps réel ligne par ligne, c'est-à-dire sans mise en mémoire tampon.
  • Des fichiers de script distincts peuvent être utilisés.
  • La magie peut être nécessaire.
TWiStErRob
la source
Quel contrôle outanderravez-vous sur le programme?
Kevin
1
@Kevin Je pense que la question est plus générique que ça. Ici, outanderrc'est juste un alias qui imprime une ligne vers stdout et une autre vers stderr. L'idée (si c'est possible) est de construire une solution générique qui pourrait fonctionner avec n'importe quel programme, sans les modifier.
lgeorget
@lgeorget Je comprends cela, mais je ne pense pas qu'il soit possible de respecter strictement toutes les contraintes dans une solution générique, alors je voyais si nous pouvions en obtenir une spécifique.
Kevin
@Igeorget a raison.
TWiStErRob

Réponses:

12
2>&1 >>outputfile | tee --append outputfile

Pour des tests faciles:

echo -n >outputfile; bash -c "echo stdout >&1; echo stderr >&2" 2>&1 >>outputfile |
  tee --append outputfile; echo "outputfile:"; cat outputfile

Modifier 1:

Cela fonctionne en écrivant stdout (uniquement) dans le fichier, en faisant sterrout stdout pour qu'il passe par le tube et en demandant à tee d'écrire sa sortie dans le même fichier.

Les deux écritures doivent être effectuées en mode ajout ( >>au lieu de >) sinon les deux écraseraient la sortie de l'autre.

Comme le tube est un tampon, rien ne garantit que la sortie apparaît dans le fichier dans le bon ordre. Cela ne changerait même pas si une application était connectée aux deux descripteurs de fichiers (deux canaux). Pour un ordre garanti, les deux sorties devraient passer par le même canal et être marquées respectivement. Ou vous auriez besoin de trucs vraiment fantaisistes:

  1. Si stdout et stderr étaient tous deux redirigés vers un fichier (pas le même fichier!) Et que les deux fichiers étaient sur un volume FUSE, le module FUSE pourrait marquer chaque écriture avec un horodatage afin qu'une deuxième application puisse trier correctement les données et les combiner pour le fichier de sortie réel. Ou vous ne marquez pas les données mais demandez au module de créer le fichier de sortie combiné. Il n'y a probablement pas encore de module FUSE qui le fasse ...
  2. Stdout et stderr pourraient être dirigés vers /dev/null. Les sorties de l'application seraient séparées en la parcourant strace -f -s 32000 -e trace=write. Vous devrez inverser l'échappement dans ce cas. Inutile de dire que l'application ne s'exécute pas plus rapidement en étant tracée.
  3. Peut-être que le même pourrait être atteint en utilisant un module FUSE simple et existant et en traçant le module au lieu de l'application. Cela peut être plus rapide que le traçage de l'application car (ou plutôt: si) le module a probablement beaucoup moins d'appels système que l'application.
  4. Si l'application elle-même peut être modifiée: l'application pourrait être arrêtée après chaque sortie (mais je pense que cela n'est possible que de l'intérieur) et continuer uniquement après avoir reçu le signal s (SIGUSR1 ou SIGCONT). La lecture de l'application à partir du tuyau devrait vérifier à la fois le tuyau et le fichier pour de nouvelles données et envoyer le signal après chaque nouvelle donnée. Selon le type d'application, cela peut être plus rapide ou même plus lent que la méthode Strace. FUSE serait la solution de vitesse maximale.
Hauke ​​Laging
la source
1
Bah. Attrapez-moi au milieu d'écrire exactement la même réponse, pourquoi ne le fais pas.
Kevin
2
NB ceci a une condition de course introduisant la possibilité de permuter / errer les lignes, mais je ne pense pas que cela puisse être évité.
Kevin
1
@Kevin Cela arrive aux meilleurs d'entre nous, j'en ai déjà souffert et j'avais presque demandé une fonctionnalité "Montrez-moi que quelqu'un écrit" (ce qui serait compliqué, cependant). Il me semble que la condition de concurrence critique ne se produit que si une écriture dans le fichier (stdout) se produit après une écriture dans le pipeline.
Hauke ​​Laging
Cela n'enverrait-il pas les deux stdoutet stderrà tee, ou est-ce que je manque quelque chose? Je pense que l'exigence du PO est de tee stderrseulement.
Joseph R.
@JosephR. Vous ne l'avez pas essayé?
Hauke ​​Laging