Sé cómo redirigir stdout a un archivo:
exec > foo.log
echo test
esto colocará la 'prueba' en el archivo foo.log.
Ahora quiero redirigir el resultado al archivo de registro Y mantenerlo en stdout
es decir, se puede hacer trivialmente desde fuera del script:
script | tee foo.log
pero quiero declararlo dentro del script mismo
Lo intenté
exec | tee foo.log
Pero no funcionó.
Respuestas:
Tenga en cuenta que esto es
bash
, nosh
. Si invoca el script consh myscript.sh
, obtendrá un error en la línea desyntax error near unexpected token '>'
.Si está trabajando con trampas de señal, es posible que desee utilizar la
tee -i
opción para evitar la interrupción de la salida si se produce una señal. (Gracias a JamesThomasMoon1979 por el comentario).Herramientas que cambian su salida dependiendo de si escriben en una tubería o en un terminal (
ls
usando colores y salida en columnas, por ejemplo) detectarán la construcción anterior como que significa que salen a una tubería.Hay opciones para aplicar la coloración / columna (p
ls -C --color=always
. Ej .). Tenga en cuenta que esto dará como resultado que los códigos de color se escriban también en el archivo de registro, lo que lo hará menos legible.fuente
tee
no se debe almacenar en búfer su salida. Si se amortigua en la mayoría de los sistemas, se rompe en la mayoría de los sistemas. Ese es un problema de lastee
implementaciones, no de mi solución.exec
es muy poderoso, pero también muy involucrado. Puede "hacer una copia de seguridad" de la salida estándar actual en un descriptor de archivo diferente y luego recuperarla más adelante. Google "tutorial de bash exec", hay muchas cosas avanzadas por ahí.exec
está documentado para no iniciar nuevos procesos,>(tee ...)
es una sustitución estándar de tubería / proceso, y&
en la redirección, por supuesto, no tiene nada que ver con el fondo ...? :-)-i
atee
. De lo contrario, las interrupciones de señal (trampas) interrumpirán stdout en el script principal. Por ejemplo, si tiene untrap 'echo foo' EXIT
y luego presionactrl+c
, no verá " foo ". Entonces modificaría la respuesta aexec &> >(tee -ia file)
.La respuesta aceptada no conserva STDERR como un descriptor de archivo separado. Eso significa
no saldrá
bar
a la terminal, solo al archivo de registro, ydará salida a ambos
foo
ybar
al terminal. Claramente, ese no es el comportamiento que un usuario normal probablemente espera. Esto se puede solucionar mediante el uso de dos procesos tee separados, ambos anexados al mismo archivo de registro:(Tenga en cuenta que lo anterior no trunca inicialmente el archivo de registro; si desea ese comportamiento, debe agregar
al principio del guión)
La especificación POSIX.1-2008 de
tee(1)
requiere que la salida no esté almacenada, es decir, ni siquiera esté almacenada en línea, por lo que en este caso es posible que STDOUT y STDERR puedan terminar en la misma línea defoo.log
; sin embargo, eso también podría suceder en el terminal, por lo que el archivo de registro será un fiel reflejo de lo que se puede ver en el terminal, si no un espejo exacto del mismo. Si desea que las líneas STDOUT se separen limpiamente de las líneas STDERR, considere usar dos archivos de registro, posiblemente con prefijos de sello de fecha en cada línea para permitir el reensamblaje cronológico más adelante.fuente
exec > >(tee -a $LOG)
trap "kill -9 $! 2>/dev/null" EXIT
exec 2> >(tee -a $LOG >&2)
trap "kill -9 $! 2>/dev/null" EXIT
-i
atee
. De lo contrario, las interrupciones de señal (trampas) interrumpirán stdout en el script. Por ejemplo, si ustedtrap 'echo foo' EXIT
y luego presionactrl+c
, no verá " foo ". Entonces modificaría la respuesta aexec > >(tee -ia foo.log)
.. log
o. log foo.log
: sam.nipl.net/sh/log sam.nipl.net/sh/log-aSTDOUT
aparecerán primero como un lote y luegoSTDERR
aparecerán los mensajes . No están intercalados como se espera habitualmente.Solución para busybox, macOS bash y shells que no son bash
La respuesta aceptada es sin duda la mejor opción para bash. Estoy trabajando en un entorno de Busybox sin acceso a bash, y no entiende la
exec > >(tee log.txt)
sintaxis. Tampoco lo haceexec >$PIPE
correctamente, al intentar crear un archivo ordinario con el mismo nombre que la canalización con nombre, que falla y se bloquea.Esperemos que esto sea útil para alguien más que no tenga bash.
Además, para cualquiera que use una tubería con nombre, es seguro
rm $PIPE
, porque eso desvincula la tubería del VFS, pero los procesos que la usan aún mantienen un recuento de referencia en ella hasta que finalizan.Tenga en cuenta que el uso de $ * no es necesariamente seguro.
fuente
Dentro de su archivo de script, coloque todos los comandos entre paréntesis, de esta manera:
fuente
{}
)Manera fácil de hacer un registro de script bash en syslog. La salida del script está disponible tanto a través
/var/log/syslog
como a través de stderr. syslog agregará metadatos útiles, incluidas las marcas de tiempo.Agregue esta línea en la parte superior:
Alternativamente, envíe el registro a un archivo separado:
Esto requiere
moreutils
(para elts
comando, que agrega marcas de tiempo).fuente
Usando la respuesta aceptada, mi script siguió regresando excepcionalmente temprano (justo después de 'exec>> (tee ...)') dejando el resto de mi script ejecutándose en segundo plano. Como no pude lograr que esa solución funcionara a mi manera, encontré otra solución / solución al problema:
Esto hace que la salida del script pase del proceso, a través de la tubería hacia el proceso de fondo secundario de 'tee' que registra todo en el disco y en el stdout original del script.
Tenga en cuenta que 'exec &>' redirige tanto stdout como stderr, podríamos redirigirlos por separado si lo deseamos, o cambiar a 'exec>' si solo queremos stdout.
Incluso si la tubería se elimina del sistema de archivos al comienzo del script, continuará funcionando hasta que finalice el proceso. Simplemente no podemos hacer referencia a él usando el nombre del archivo después de la línea rm.
fuente
$logfile
parte detee < ${logfile}.pipe $logfile &
. Específicamente, traté de alterar esto para capturar líneas de registro de comando expandidas completas (desdeset -x
) al archivo mientras solo mostraba líneas sin llevar '+' en stdout al cambiar(tee | grep -v '^+.*$') < ${logfile}.pipe $logfile &
pero recibí un mensaje de error al respecto$logfile
. ¿Puedes explicar latee
línea con un poco más de detalle?Bash 4 tiene un
coproc
comando que establece una canalización con nombre a un comando y le permite comunicarse a través de él.fuente
No puedo decir que me sienta cómodo con ninguna de las soluciones basadas en exec. Prefiero usar tee directamente, por lo que hago que el script se llame a sí mismo con tee cuando lo solicite:
Esto le permite hacer esto:
Puede personalizar esto, por ejemplo, hacer que tee = false sea el valor predeterminado en su lugar, hacer que TEE retenga el archivo de registro, etc. Supongo que esta solución es similar a la de jbarlow, pero más simple, tal vez la mía tiene limitaciones que aún no he encontrado.
fuente
Ninguno de estos es una solución perfecta, pero aquí hay un par de cosas que puedes probar:
o
El segundo dejaría un archivo de tubería alrededor si algo sale mal con su script, lo que puede o no ser un problema (es decir, tal vez podría
rm
hacerlo en el shell principal después).fuente
tee
. He editado. Como dije, ninguna de las dos es una solución perfecta, pero los procesos en segundo plano se anularán cuando finalice su shell principal, por lo que no tiene que preocuparse de que acaparen los recursos para siempre.