example - shell scripting pdf
Cómo agregar la marca de tiempo a la redirección de STDERR (12)
¿En bash / ksh podemos agregar la marca de tiempo al redireccionamiento de STDERR?
Eg myscript.sh 2> error.log
Quiero que se escriba una marca de tiempo también en el registro.
¿Qué hay de timestamping la salida restante, redirigiendo todo a stdout?
Esta respuesta combina algunas técnicas de arriba, así como de unix stackexchange here y here . bash
> = 4.2
se supone, pero otros shells avanzados pueden funcionar. Para < 4.2
, reemplace printf
con una llamada (más lenta) hasta la date
.
: ${TIMESTAMP_FORMAT:="%F %T"} # override via environment
_loglines() {
while IFS= read -r _line ; do
printf "%(${TIMESTAMP_FORMAT})T#%s/n" ''-1'' "$_line";
done;
}
exec 7<&2 6<&1
exec &> >( _loglines )
# Logit
Para restaurar stdout / stderr:
exec 1>&6 2>&7
A continuación, puede usar tee
para enviar las marcas de tiempo a stdout
y a un archivo de registro.
_tmpfile=$(mktemp)
exec &> >( _loglines | tee $_tmpfile )
No es una mala idea tener código de limpieza si el proceso salió sin error:
trap "_cleanup /$?" 0 SIGHUP SIGINT SIGABRT SIGBUS SIGQUIT SIGTRAP SIGUSR1 SIGUSR2 SIGTERM
_cleanup() {
exec >&6 2>&7
[[ "$1" != 0 ]] && cat "$_logtemp"
rm -f "$_logtemp"
exit "${1:-0}"
}
Aquí hay una versión que usa un ciclo de while read
while como pax , pero no requiere descriptores de archivo adicionales o un script separado (aunque puede usar uno). Utiliza la sustitución de procesos:
myscript.sh 2> >( while read line; do echo "$(date): ${line}"; done > error.log )
Usando predate.sh
de pax :
myscript.sh 2> >( predate.sh > error.log )
El paquete devscripts
en Debian / Ubuntu contiene un script llamado annotate-output
que hace eso (para stdout y stderr).
$ annotate-output make
21:41:21 I: Started make
21:41:21 O: gcc -Wall program.c
21:43:18 E: program.c: Couldn''t compile, and took me ages to find out
21:43:19 E: collect2: ld returned 1 exit status
21:43:19 E: make: *** [all] Error 1
21:43:19 I: Finished with exitcode 2
El programa está en la entrada estándar del paquete moreutils para la salida estándar, y prefija cada línea con una marca de tiempo.
Para prefijar líneas stdout: command | ts
command | ts
Para prefijar tanto stdout como stderr: command 2>&1 | ts
command 2>&1 | ts
En lugar de escribir una secuencia de comandos para canalizar, prefiero escribir el registrador como una función dentro de la secuencia de comandos, y luego enviar la totalidad del proceso con corchetes, así:
# Vars
logfile=/path/to/scriptoutput.log
# Defined functions
teelogger(){
log=$1
while read line ; do
print "$(date +"%x %T") :: $line" | tee -a $log
done
}
# Start process
{
echo ''well''
sleep 3
echo ''hi''
sleep 3
echo ''there''
sleep 3
echo ''sailor''
} | teelogger $logfile
Era demasiado flojo para todas las soluciones actuales ... Así que descubrí uno nuevo (también se pueden ajustar los trabajos para stdout
para stderr
):
echo "output" | xargs -L1 -I{} bash -c "echo /$(date +''%x %T'') ''{}''" | tee error.log
guardaría para archivar e imprimir algo así: 11/3/16 16:07:52 output
Detalles:
-L1
significa "para cada nueva línea"
-I{}
significa "reemplazar {} por entrada"
bash -c
se usa para actualizar $(date)
cada vez para una nueva llamada
%x %T
formatea la marca de tiempo en forma mínima.
Debería funcionar como un amuleto si stdout
y stderr
no tienen comillas ("o`). Si tiene (o podría tener), es mejor usar:
echo "output" | awk ''{cmd="(date +''%H:%M:%S'')"; cmd | getline d; print d,$0; close(cmd)} | tee error.log''
(Tomé de mi respuesta en otro tema: https://.com/a/41138870/868947 )
Esto: nohup myscript.sh 2>> (while read line; echo "$ (date): $ {line}"; done> mystd.err) </ dev / null &
Funciona como tal, pero cuando cierro la sesión y vuelvo a iniciar sesión en el servidor, no funciona. es decir, mystd.err deja de poblarse con la corriente stderr aunque mi proceso (myscript.sh aquí) todavía se ejecuta.
¿Alguien sabe cómo recuperar la stderr perdida en el archivo mystd.err?
Me gustan esos scripts de shell portátiles, pero un poco molestos porque teclean / exec date (1) para cada línea. Aquí hay un rápido perl one-liner para hacer lo mismo de manera más eficiente:
perl -p -MPOSIX -e ''BEGIN {$!=1} $_ = strftime("%T ", localtime) . $_''
Para usarlo, simplemente ingrese esta entrada de comando a través de su stdin:
(echo hi; sleep 1; echo lo) | perl -p -MPOSIX -e ''BEGIN {$|=1} $_ = strftime("%T ", localtime) . $_''
Pensé que agregaría mi valor de 2 centavos.
#!/bin/sh
timestamp(){
name=$(printf "$1%*s" `expr 15 - ${#1}`)
awk "{ print strftime(/"%b %d %H:%M:%S/"), /"- $name -/", $$, /"- INFO -/", /$0; fflush() }";
}
echo "hi" | timestamp "process name" >> /tmp/proccess.log
printf "$ 1% * s" `expr 15 - $ {# 1}`
Separar el nombre para que se vea bien, donde 15 es el espacio máximo emitido, aumentar si se desea
salidas >> Fecha - Nombre del proceso - ID del proceso - INFO - Mensaje
Jun 27 13:57:20 - process name - 18866 - INFO - hi
Si desea redirigir a stdout, descubrí que debe hacer esto:
myscript.sh >> >( while read line; do echo "$(date): ${line}"; done )
No estoy seguro de por qué necesito el >
delante del (
, entonces <(
, pero eso es lo que funciona.
Si está hablando de una marca de tiempo actualizada en cada línea, eso es algo que probablemente quiera hacer en su secuencia de comandos real (pero consulte a continuación una solución ingeniosa si no tiene poder para cambiarla). Si solo quiere una fecha de marcador en su propia línea antes de que su script comience a escribir, usaría:
( date 1>&2 ; myscript.sh ) 2>error.log
Lo que necesita es un truco para canalizar stderr a través de otro programa que puede agregar marcas de tiempo en cada línea. Podrías hacer esto con un programa en C, pero hay una forma mucho más desviada usando solo bash
.
Primero, cree una secuencia de comandos que agregará la marca de tiempo a cada línea (llamada predate.sh
):
#!/bin/bash
while read line ; do
echo "$(date): ${line}"
done
Por ejemplo:
( echo a ; sleep 5 ; echo b ; sleep 2 ; echo c ) | ./predate.sh
produce:
Fri Oct 2 12:31:39 WAST 2009: a
Fri Oct 2 12:31:44 WAST 2009: b
Fri Oct 2 12:31:46 WAST 2009: c
Entonces necesitas otro truco que pueda cambiar stdout y stderr, esta pequeña monstruosidad aquí:
( myscript.sh 3>&1 1>&2- 2>&3- )
Entonces es simple combinar los dos trucos stdout
tiempo stdout
y redirigiéndolo a su archivo:
( myscript.sh 3>&1 1>&2- 2>&3- ) | ./predate.sh >error.log
La siguiente transcripción muestra esto en acción:
pax> cat predate.sh
#!/bin/bash
while read line ; do
echo "$(date): ${line}"
done
pax> cat tstdate.sh
#!/bin/bash
echo a to stderr then wait five seconds 1>&2
sleep 5
echo b to stderr then wait two seconds 1>&2
sleep 2
echo c to stderr 1>&2
echo d to stdout
pax> ( ( ./tstdate.sh ) 3>&1 1>&2- 2>&3- ) | ./predate.sh >error.log
d to stdout
pax> cat error.log
Fri Oct 2 12:49:40 WAST 2009: a to stderr then wait five seconds
Fri Oct 2 12:49:45 WAST 2009: b to stderr then wait two seconds
Fri Oct 2 12:49:47 WAST 2009: c to stderr
Como ya se mencionó, predate.sh
prefijará cada línea con una marca de tiempo y tstdate.sh
es simplemente un programa de prueba para escribir en stdout
y stderr
con intervalos de tiempo específicos.
Cuando ejecuta el comando, en realidad se escribe "d to stdout"
en stderr (pero ese es su dispositivo TTY o cualquier cosa que haya sido stdout
cuando comenzó). Las líneas stderr
marca de tiempo se escriben en su archivo deseado.
#!/bin/bash
DEBUG=1
LOG=$HOME/script_${0##*/}_$(date +%Y.%m.%d-%H.%M.%S-%N).log
ERROR=$HOME/error.log
exec 2> $ERROR
exec 1> >(tee -ai $LOG)
if [ $DEBUG = 0 ] ; then
exec 4> >(xargs -i echo -e "[ debug ] {}")
else
exec 4> /dev/null
fi
# test
echo " debug sth " >&4
echo " log sth normal "
type -f this_is_error
echo " errot sth ..." >&2
echo " finish ..." >&2>&4
# close descriptor 4
exec 4>&-