¿Cómo redirigir stdout y stderr a un archivo y mostrar stderr a la consola?

18

Sé cómo redirigir a un archivo y usar tee; en un nivel básico Entonces

$ 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 pregunta es: qué escribir en lugar de los signos de interrogación para obtener el resultado a continuación:

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

Constaints:

  • Asumiendo bash.
  • El pedido debe mantenerse en el archivo.
  • Los contenidos de stderr se muestran en tiempo real línea por línea, es decir, sin almacenamiento en búfer.
  • Se pueden usar archivos de script separados.
  • La magia puede ser necesaria.
TWiStErRob
fuente
¿Cuánto control del outanderrprograma tienes?
Kevin
1
@ Kevin Creo que la pregunta es más genérica que eso. Aquí, outanderrsolo hay un alias que imprime una línea en stdout y otra en stderr. La idea (si es posible) es crear una solución genérica que pueda funcionar con cualquier programa, sin modificarlos.
lgeorget
@lgeorget Entiendo eso, pero no creo que sea posible cumplir estrictamente todas las restricciones en una solución genérica, por lo que estaba viendo si podíamos obtener una específica.
Kevin
@Igeorget tiene razón.
TWiStErRob

Respuestas:

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

Para pruebas fáciles:

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

Editar 1:

Esto funciona escribiendo stdout (solo) en el archivo, haciendo stdout más estéril para que atraviese la tubería y haciendo que tee escriba su salida en el mismo archivo.

Ambas escrituras se deben hacer en modo anexar (en >>lugar de >), de lo contrario, ambas sobrescribirán la salida de las demás.

Como la tubería es un búfer, no hay garantía de que la salida aparezca en el archivo en el orden correcto. Esto ni siquiera cambiaría si una aplicación estuviera conectada a ambos descriptores de archivo (dos canales). Para un orden garantizado, ambas salidas tendrían que pasar por el mismo canal y estar marcadas respectivamente. O necesitarías algunas cosas realmente elegantes:

  1. Si tanto stdout como stderr se redirigieron a un archivo (¡no es el mismo archivo!) Y ambos archivos estaban en un volumen FUSE, entonces el módulo FUSE podría marcar cada escritura individual con una marca de tiempo para que una segunda aplicación pudiera clasificar los datos correctamente y combinarlos para el archivo de salida real. O no marca los datos pero hace que el módulo cree el archivo de salida combinado. Lo más probable es que todavía no haya un módulo FUSE que haga esto ...
  2. Tanto stdout como stderr podrían ser dirigidos a /dev/null. Las salidas de la aplicación se separarían ejecutándola strace -f -s 32000 -e trace=write. Tendría que revertir el escape en ese caso. No es necesario decir que la aplicación no se ejecuta más rápido al rastrearse.
  3. Quizás podría lograrse lo mismo utilizando un módulo FUSE simple existente y rastreando el módulo en lugar de la aplicación. Esto puede ser más rápido que rastrear la aplicación porque (o más bien: si) el módulo probablemente tenga muchas menos llamadas al sistema que la aplicación.
  4. Si la aplicación en sí puede modificarse: la aplicación podría detenerse después de cada salida (pero creo que esto es posible solo desde adentro) y continuar solo después de recibir la señal s (SIGUSR1 o SIGCONT). La lectura de la aplicación de la tubería tendría que verificar tanto la tubería como el archivo en busca de nuevos datos y enviar la señal después de cada nuevo dato. Dependiendo del tipo de aplicación, esto puede ser más rápido o incluso más lento que el método strace. FUSE sería la solución de velocidad máxima.
Hauke ​​Laging
fuente
1
Bah. Atrápame en el medio de escribir exactamente la misma respuesta, ¿por qué no?
Kevin
2
Nota: esto tiene una condición de carrera que introduce la posibilidad de intercambiar / errar líneas, pero no creo que se pueda evitar.
Kevin
1
@Kevin Eso nos sucede a los mejores, ya lo he sufrido antes y casi había pedido una función de "muéstrame que alguien está escribiendo" (aunque sería complicado). Me parece que la condición de carrera ocurre solo si se produce una escritura en el archivo (stdout) después de una escritura en la tubería.
Hauke ​​Laging
No habría que enviar tanto stdouty stderrque tee, o me estoy perdiendo algo? Creo que el requisito del OP es tee stderrsolo.
Joseph R.
@JosephR. ¿No has probado eso?
Hauke ​​Laging