Quiero redirigir stdout y stderr de un proceso a un solo archivo. ¿Cómo hago eso en Bash?
Quiero redirigir stdout y stderr de un proceso a un solo archivo. ¿Cómo hago eso en Bash?
Respuestas:
Echa un vistazo aquí . Debiera ser:
yourcommand &>filename
(redirecciona a ambos stdout
y stderr
al nombre del archivo).
#!/bin/bash
más que con #!/bin/sh
, ya que requiere bash.
do_something 2>&1 | tee -a some_file
Esto va a redirigir stderr a stdout y stdout some_file
e imprimirlo en stdout.
do_something &>filename
no. +1.
Ambiguous output redirect.
alguna idea de por qué?
$?
ya no se refiere al estado de salida de do_something
, sino al estado de salida de tee
.
Puede redirigir stderr a stdout y stdout a un archivo:
some_command >file.log 2>&1
Ver http://tldp.org/LDP/abs/html/io-redirection.html
Se prefiere este formato que el formato &> más popular que solo funciona en bash. En Bourne Shell se podría interpretar como ejecutar el comando en segundo plano. Además, el formato es más legible 2 (es STDERR) redirigido a 1 (STDOUT).
EDITAR: cambió el orden como se señala en los comentarios
# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-
# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE
# Redirect STDERR to STDOUT
exec 2>&1
echo "This line will appear in $LOG_FILE, not 'on screen'"
Ahora, echo simple escribirá en $ LOG_FILE. Útil para demonizar.
Para el autor de la publicación original,
Depende de lo que necesites lograr. Si solo necesita redirigir dentro / fuera de un comando que llama desde su script, las respuestas ya están dadas. El mío se trata de redirigir dentro del script actual que afecta a todos los comandos / incorporados (incluye bifurcaciones) después del fragmento de código mencionado.
Otra solución interesante es redirigir a std-err / out Y al registrador o al archivo de registro a la vez, lo que implica dividir "una secuencia" en dos. Esta funcionalidad es proporcionada por el comando 'tee' que puede escribir / agregar a varios descriptores de archivos (archivos, tomas, tuberías, etc.) a la vez: tee FILE1 FILE2 ...> (cmd1)> (cmd2) ...
exec 3>&1 4>&2 1> >(tee >(logger -i -t 'my_script_tag') >&3) 2> >(tee >(logger -i -t 'my_script_tag') >&4)
trap 'cleanup' INT QUIT TERM EXIT
get_pids_of_ppid() {
local ppid="$1"
RETVAL=''
local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
RETVAL="$pids"
}
# Needed to kill processes running in background
cleanup() {
local current_pid element
local pids=( "$$" )
running_pids=("${pids[@]}")
while :; do
current_pid="${running_pids[0]}"
[ -z "$current_pid" ] && break
running_pids=("${running_pids[@]:1}")
get_pids_of_ppid $current_pid
local new_pids="$RETVAL"
[ -z "$new_pids" ] && continue
for element in $new_pids; do
running_pids+=("$element")
pids=("$element" "${pids[@]}")
done
done
kill ${pids[@]} 2>/dev/null
}
Entonces, desde el principio. Supongamos que tenemos una terminal conectada a / dev / stdout (FD # 1) y / dev / stderr (FD # 2). En la práctica, podría ser una tubería, un enchufe o lo que sea.
El resultado de ejecutar un script con la línea anterior y, además, esta:
echo "Will end up in STDOUT(terminal) and /var/log/messages"
...es como sigue:
$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages
$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(terminal) and /var/log/messages
Si desea ver una imagen más clara, agregue estas 2 líneas al script:
ls -l /proc/self/fd/
ps xf
bash your_script.sh 1>file.log 2>&1
1>file.log
indica al shell que envíe STDOUT al archivo file.log
y 2>&1
le dice que redirija STDERR (descriptor de archivo 2) a STDOUT (descriptor de archivo 1).
Nota: El orden importa, como señaló liw.fi, 2>&1 1>file.log
no funciona.
Curiosamente, esto funciona:
yourcommand &> filename
Pero esto da un error de sintaxis:
yourcommand &>> filename
syntax error near unexpected token `>'
Tienes que usar:
yourcommand 1>> filename 2>&1
&>>
parece funcionar en BASH 4:$ echo $BASH_VERSION 4.1.5(1)-release $ (echo to stdout; echo to stderr > /dev/stderr) &>> /dev/null
Respuesta corta: Command >filename 2>&1
oCommand &>filename
Explicación:
Considere el siguiente código que imprime la palabra "stdout" en stdout y la palabra "stderror" en stderror.
$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror
Tenga en cuenta que el operador '&' le dice a bash que 2 es un descriptor de archivo (que apunta al stderr) y no un nombre de archivo. Si omitimos '&', este comando se imprimirá stdout
en stdout y creará un archivo llamado "2" y escribirástderror
allí.
Al experimentar con el código anterior, puede ver por sí mismo exactamente cómo funcionan los operadores de redirección. Por ejemplo, al cambiar qué archivo, cuál de los dos descriptores 1,2
, se redirige a /dev/null
las siguientes dos líneas de código, elimine todo de stdout y todo de stderror respectivamente (imprimiendo lo que queda).
$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout
Ahora, podemos explicar por qué la solución por qué el siguiente código no produce salida:
(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1
Para comprender realmente esto, le recomiendo que lea esta página web en las tablas de descriptores de archivos . Suponiendo que haya hecho esa lectura, podemos continuar. Tenga en cuenta que Bash procesa de izquierda a derecha; por lo tanto, Bash ve >/dev/null
primero (que es lo mismo que 1>/dev/null
) y establece el descriptor de archivo 1 para que apunte a / dev / null en lugar de stdout. Una vez hecho esto, Bash se mueve hacia la derecha y ve 2>&1
. Esto configura el descriptor de archivo 2 para que apunte al mismo archivo que el descriptor de archivo 1 (y no al descriptor de archivo 1 en sí mismo !!!! (vea este recurso en punterospara más información)) . Como el descriptor de archivo 1 apunta a / dev / null, y el descriptor de archivo 2 apunta al mismo archivo que el descriptor de archivo 1, el descriptor de archivo 2 ahora también apunta a / dev / null. Por lo tanto, ambos descriptores de archivo apuntan a / dev / null, y esta es la razón por la cual no se procesa ningún resultado.
Para probar si realmente comprende el concepto, intente adivinar el resultado cuando cambiemos el orden de redireccionamiento:
(echo "stdout"; echo "stderror" >&2) 2>&1 >/dev/null
stderror
El razonamiento aquí es que al evaluar de izquierda a derecha, Bash ve 2> & 1 y, por lo tanto, establece el descriptor de archivo 2 para que apunte al mismo lugar que el descriptor de archivo 1, es decir, stdout. Luego establece el descriptor de archivo 1 (recuerde que> / dev / null = 1> / dev / null) para apuntar a> / dev / null, eliminando así todo lo que normalmente se enviaría a la salida estándar. Por lo tanto, todo lo que nos queda es aquello que no se envió a stdout en la subshell (el código entre paréntesis), es decir, "stderror". Lo interesante a tener en cuenta es que a pesar de que 1 es solo un puntero al stdout, la redirección del puntero 2 a 1 a través de 2>&1
NO forma una cadena de punteros 2 -> 1 -> stdout. Si lo hizo, como resultado de redirigir 1 a / dev / null, el código2>&1 >/dev/null
daría a la cadena de puntero 2 -> 1 -> / dev / null, y así el código no generaría nada, en contraste con lo que vimos anteriormente.
Finalmente, señalaría que hay una manera más simple de hacer esto:
De la sección 3.6.4 aquí , vemos que podemos usar el operador &>
para redirigir tanto stdout como stderr. Por lo tanto, para redirigir la salida stderr y stdout de cualquier comando a \dev\null
(que elimina la salida), simplemente escribimos
$ command &> /dev/null
o, en el caso de mi ejemplo:
$ (echo "stdout"; echo "stderror" >&2) &>/dev/null
Conclusiones clave:
2>&1 >/dev/null
es! = >/dev/null 2>&1
. ¡Uno genera salida y el otro no!Finalmente, eche un vistazo a estos excelentes recursos:
Documentación de Bash sobre redireccionamiento , una explicación de las tablas de descriptores de archivos , introducción a punteros
LOG_FACILITY="local7.notice"
LOG_TOPIC="my-prog-name"
LOG_TOPIC_OUT="$LOG_TOPIC-out[$$]"
LOG_TOPIC_ERR="$LOG_TOPIC-err[$$]"
exec 3>&1 > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" )
exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" )
Está relacionado: Escribir stdOut & stderr en syslog.
Casi funciona, pero no de xinted; (
Quería una solución para tener el resultado de stdout plus stderr escrito en un archivo de registro y stderr todavía en la consola. Así que necesitaba duplicar la salida stderr a través de tee.
Esta es la solución que encontré:
command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
Para la situación, cuando es necesaria la "tubería", puede usar:
| &
Por ejemplo:
echo -ne "15\n100\n"|sort -c |& tee >sort_result.txt
o
TIMEFORMAT=%R;for i in `seq 1 20` ; do time kubectl get pods |grep node >>js.log ; done |& sort -h
Estas soluciones basadas en bash pueden canalizar STDOUT y STDERR por separado (de STDERR de "sort -c" o de STDERR a "sort -h").
Las siguientes funciones se pueden utilizar para automatizar el proceso de alternar salidas entre stdout / stderr y un archivo de registro.
#!/bin/bash
#set -x
# global vars
OUTPUTS_REDIRECTED="false"
LOGFILE=/dev/stdout
# "private" function used by redirect_outputs_to_logfile()
function save_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
exec 3>&1
exec 4>&2
trap restore_standard_outputs EXIT
}
# Params: $1 => logfile to write to
function redirect_outputs_to_logfile {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
save_standard_outputs
exec 1>>${LOGFILE%.log}.log
exec 2>&1
OUTPUTS_REDIRECTED="true"
}
# "private" function used by save_standard_outputs()
function restore_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
exit 1;
fi
exec 1>&- #closes FD 1 (logfile)
exec 2>&- #closes FD 2 (logfile)
exec 2>&4 #restore stderr
exec 1>&3 #restore stdout
OUTPUTS_REDIRECTED="false"
}
Ejemplo de uso dentro del script:
echo "this goes to stdout"
redirect_outputs_to_logfile /tmp/one.log
echo "this goes to logfile"
restore_standard_outputs
echo "this goes to stdout"
@ fernando-fabreti
Además de lo que hiciste, cambié ligeramente las funciones y eliminé el cierre & y funcionó para mí.
function saveStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
exec 3>&1
exec 4>&2
trap restoreStandardOutputs EXIT
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
}
# Params: $1 => logfile to write to
function redirectOutputsToLogfile {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
saveStandardOutputs
exec 1>>${LOGFILE}
exec 2>&1
OUTPUTS_REDIRECTED="true"
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
}
function restoreStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
exec 1>&3 #restore stdout
exec 2>&4 #restore stderr
OUTPUTS_REDIRECTED="false"
fi
}
LOGFILE_NAME="tmp/one.log"
OUTPUTS_REDIRECTED="false"
echo "this goes to stdout"
redirectOutputsToLogfile $LOGFILE_NAME
echo "this goes to logfile"
echo "${LOGFILE_NAME}"
restoreStandardOutputs
echo "After restore this goes to stdout"
En situaciones en las que considera usar cosas como, exec 2>&1
me parece más fácil de leer si es posible reescribir el código usando funciones bash como esta:
function myfunc(){
[...]
}
myfunc &>mylog.log