Come reindirizzare stdout e stderr su un file e visualizzare stderr su console?


18

So come reindirizzare a un file e usare tee; a livello base. Così

$ 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 domanda è: cosa scrivere al posto dei punti interrogativi per ottenere l'output di seguito:

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

Constaints:

  • Supponendo bash.
  • L'ordine dovrebbe essere mantenuto nel file.
  • i contenuti di stderr vengono visualizzati in tempo reale riga per riga, ovvero senza buffering.
  • È possibile utilizzare file di script separati.
  • La magia può essere necessaria.

Quanto controllo outanderrhai del programma?
Kevin,

1
@Kevin Penso che la domanda sia più generica di così. Qui, outanderrè solo un alias che stampa una riga su stdout e un'altra su stderr. L'idea (se possibile) è quella di costruire una soluzione generica che possa funzionare con qualsiasi programma, senza modificarli.
Lgeorget,

@lgeorget Lo capisco, ma non credo che sia possibile soddisfare rigorosamente tutti i vincoli in una soluzione generica, quindi stavo vedendo se potessimo ottenerne uno specifico.
Kevin,

@Igeorget ha ragione.
TWiStErRob,

Risposte:


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

Per test facili:

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

Modifica 1:

Funziona scrivendo stdout (solo) sul file, eseguendo stdout in modo che passi attraverso la pipe e facendo scrivere a T il suo output nello stesso file.

Entrambe le scritture devono essere eseguite in modalità append ( >>anziché >), altrimenti entrambe sovrascriverebbero l'output dell'altro.

Poiché la pipe è un buffer, non vi è alcuna garanzia che l'output appaia nel file nel giusto ordine. Ciò non cambierebbe nemmeno se un'applicazione fosse connessa a entrambi i descrittori di file (due pipe). Per un ordine garantito, entrambe le uscite dovrebbero passare attraverso lo stesso canale ed essere contrassegnate rispettivamente. O avresti bisogno di alcune cose davvero fantasiose:

  1. Se sia stdout che stderr fossero reindirizzati a un file (non lo stesso file!) Ed entrambi i file fossero su un volume FUSE, il modulo FUSE potrebbe contrassegnare ogni singola scrittura con un timestamp in modo che una seconda applicazione possa ordinare i dati correttamente e combinarli per il file di output reale. Oppure non contrassegnare i dati ma il modulo crea il file di output combinato. Molto probabilmente non esiste ancora un modulo FUSE che fa questo ...
  2. Sia stdout che stderr potrebbero essere indirizzati a /dev/null. Gli output dell'applicazione verrebbero separati eseguendola strace -f -s 32000 -e trace=write. In questo caso dovresti invertire la fuga. Inutile dire che l'applicazione non viene eseguita più velocemente per essere rintracciata.
  3. Forse lo stesso potrebbe essere raggiunto utilizzando un semplice modulo FUSE esistente e tracciando il modulo anziché l'applicazione. Questo potrebbe essere più veloce del tracciamento dell'applicazione perché (o meglio: se) il modulo probabilmente ha molto meno syscalls rispetto all'applicazione.
  4. Se l'applicazione stessa può essere modificata: l'app potrebbe essere arrestata dopo ogni uscita (ma penso che ciò sia possibile solo dall'interno) e continuare solo dopo aver ricevuto il segnale s (SIGUSR1 o SIGCONT). L'applicazione che legge dalla pipe dovrebbe controllare sia la pipe che il file per i nuovi dati e inviare il segnale dopo ogni nuovo dato. A seconda del tipo di applicazione, questo può essere più veloce o addirittura più lento del metodo Strace. FUSE sarebbe la soluzione per la massima velocità.

1
Bah. Catch me nel mezzo di scrivere esattamente la stessa risposta perché non farlo.
Kevin,

2
NB questo ha una condizione di gara che introduce la possibilità di scambiare / errare le righe, ma non credo che possa essere evitato.
Kevin,

1
@Kevin Quello che succede al meglio di noi, ne ho sofferto prima e avevo quasi chiesto una funzione "mostrami che qualcuno sta scrivendo" (il che sarebbe complicato, però). Mi sembra che la race condition si verifichi solo se una scrittura sul file (stdout) si verifica dopo una scrittura sulla pipeline.
Hauke ​​Laging,

Questo non manderebbe entrambi stdoute stderrverso tee, o mi sto perdendo qualcosa? Penso che il requisito del PO sia tee stderrsolo.
Joseph R.,

@JosephR. Non ti è capitato di provarlo?
Hauke ​​Laging,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.