Tengo un script que será ejecutado de forma interactiva por usuarios no técnicos. El script escribe actualizaciones de estado en STDOUT para que el usuario pueda estar seguro de que el script se está ejecutando correctamente.
Quiero que tanto STDOUT como STDERR sean redirigidos al terminal (para que el usuario pueda ver que el script está funcionando y ver si hubo un problema). También quiero que ambas secuencias se redirijan a un archivo de registro.
He visto un montón de soluciones en la red. Algunos no funcionan y otros son horriblemente complicados. Desarrollé una solución viable (que ingresaré como respuesta), pero es torpe.
La solución perfecta sería una sola línea de código que podría incorporarse al principio de cualquier script que envíe ambos flujos tanto al terminal como a un archivo de registro.
EDITAR: Redirigir STDERR a STDOUT y canalizar el resultado a tee funciona, pero depende de que los usuarios recuerden redirigir y canalizar la salida. Quiero que el registro sea infalible y automático (por eso me gustaría poder insertar la solución en el script).
Respuestas:
Utilice "tee" para redirigir a un archivo y la pantalla. Dependiendo del shell que use, primero debe redirigir stderr a stdout usando
o
En csh, hay un comando incorporado llamado "script" que capturará todo lo que vaya a la pantalla en un archivo. Comience escribiendo "script", luego haga lo que quiera capturar, luego presione control-D para cerrar el archivo de script. No conozco un equivalente para sh / bash / ksh.
Además, dado que ha indicado que estos son sus propios scripts sh que puede modificar, puede hacer la redirección internamente rodeando todo el script con llaves o corchetes, como
fuente
2>&1 | tee -a filename
no estaba guardando stderr en el archivo de mi script, ¡pero funcionó bien cuando copié el comando y lo pegué en la terminal! Sin embargo, el truco del corchete funciona bien.Aproximadamente media década después ...
Creo que esta es la "solución perfecta" que busca el OP.
Aquí hay una línea que puede agregar a la parte superior de su script Bash:
Aquí hay un pequeño script que demuestra su uso:
(Nota: Esto sólo funciona con Bash Lo hará. No trabajo con / bin / sh).
Adaptado de aquí ; el original, por lo que puedo decir, no captó STDERR en el archivo de registro. Corregido con una nota de aquí .
fuente
El patrón
Esto redirige tanto stdout como stderr por separado, y envía copias separadas de stdout y stderr a la persona que llama (que podría ser su terminal).
En zsh, no pasará a la siguiente instrucción hasta que el
tee
s haya terminado.En bash, puede encontrar que las últimas líneas de salida aparecen después de cualquier declaración que venga a continuación.
En cualquier caso, los bits correctos van a los lugares correctos.
Explicación
Aquí hay un script (almacenado en ./example):
Aquí tienes una sesión:
Así es como funciona:
tee
Se inician ambos procesos, sus stdins se asignan a descriptores de archivo. Debido a que están encerrados en sustituciones de procesos , las rutas a esos descriptores de archivo se sustituyen en el comando de llamada, por lo que ahora se ve así:the_cmd 1> /proc/self/fd/13 2> /proc/self/fd/14
the_cmd
se ejecuta, escribiendo stdout en el primer descriptor de archivo y stderr en el segundo.En el caso de bash, una vez que
the_cmd
finaliza, la siguiente declaración ocurre inmediatamente (si su terminal es la persona que llama, verá aparecer su mensaje).En el caso de zsh, una vez que
the_cmd
finaliza, el shell esperatee
a que terminen ambos procesos antes de continuar. Más sobre esto aquí .El primer
tee
proceso, que está leyendo desdethe_cmd
la salida estándar, escribe una copia de esa salida estándar en la persona que llama porque eso es lo quetee
hace. Sus salidas no se redirigen, por lo que vuelven a la persona que llama sin cambiosEl segundo
tee
proceso lostdout
redirige a la personastderr
que llama (lo cual es bueno, porque stdin está leyendo dethe_cmd
stderr). Entonces, cuando escribe en su stdout, esos bits van al stderr de la persona que llama.Esto mantiene stderr separado de stdout tanto en los archivos como en la salida del comando.
Si el primer tee escribe algún error, aparecerán tanto en el archivo stderr como en el stderr del comando, si el segundo tee escribe algún error, solo aparecerán en el stderr del terminal.
fuente
tee
está disponible en el sistema en cuestión). El error que aparece es "El proceso no puede acceder al archivo porque está siendo utilizado por otro proceso".para redirigir stderr a stdout agregue esto a su comando:
2>&1
Para enviar a la terminal e iniciar sesión en el archivo, debe usartee
Ambos juntos se verían así:
EDITAR: para incrustarlo en su script, haría lo mismo. Entonces tu guión
terminaría como
fuente
EDITAR: Veo que me descarrilé y terminé respondiendo una pregunta diferente a la que me hicieron. La respuesta a la pregunta real está al final de la respuesta de Paul Tomblin. (Si desea mejorar esa solución para redirigir stdout y stderr por separado por alguna razón, puede usar la técnica que describo aquí).
Quería una respuesta que conservara la distinción entre stdout y stderr. Desafortunadamente, todas las respuestas dadas hasta ahora que preservan esa distinción son propensas a la raza: corren el riesgo de que los programas vean una entrada incompleta, como señalé en los comentarios.
Creo que finalmente encontré una respuesta que conserva la distinción, que no es propensa a la raza y tampoco es terriblemente complicada.
Primer bloque de construcción: para intercambiar stdout y stderr:
Segundo bloque de construcción: si quisiéramos filtrar (por ejemplo, tee) solo stderr, podríamos lograrlo cambiando stdout y stderr, filtrando y luego volviendo a intercambiar:
Ahora el resto es fácil: podemos agregar un filtro de salida estándar, ya sea al principio:
o al final:
Para convencerme de que los dos comandos anteriores funcionan, utilicé lo siguiente:
La salida es:
y mi mensaje vuelve inmediatamente después de "
teed stderr: to stderr
", como se esperaba.Nota al pie sobre zsh :
La solución anterior funciona en bash (y tal vez en otros shells, no estoy seguro), pero no funciona en zsh. Hay dos razones por las que falla en zsh:
2>&3-
zsh no entiende la sintaxis ; que tiene que ser reescrito como2>&3 3>&-
Entonces, por ejemplo, mi segunda solución debe reescribirse para zsh como
{my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(que también funciona en bash, pero es muy detallado).Por otro lado, puede aprovechar el misterioso tee implícito incorporado de zsh para obtener una solución mucho más corta para zsh, que no ejecuta tee en absoluto:
(No habría adivinado por los documentos que encontré que
>&1
y2>&2
son lo que desencadena el tee implícito de zsh; lo descubrí por prueba y error).fuente
my_function
) en el filtrado stdout / stderr anidado? Lo hice,{ { my_function || touch failed;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
pero se siente extraño crear un archivo como indicador de falla ...Use el
script
comando en su script (script man 1)Cree un shellscript contenedor (2 líneas) que configure script () y luego llame a exit.
Parte 1: wrap.sh
Parte 2: realscript.sh
Resultado:
fuente
Utilice el programa tee y dup stderr para stdout.
fuente
Creé un script llamado "RunScript.sh". El contenido de este script es:
Lo llamo así:
Esto funciona, pero requiere que los scripts de la aplicación se ejecuten a través de un script externo. Es un poco torpe.
fuente
Un año después, aquí hay un antiguo script de bash para registrar cualquier cosa. Por ejemplo,
teelog make ...
registros a un nombre de registro generado (y vea el truco para registrar correos electrónicos anidadosmake
también).fuente