Pregunta Redirige stderr y stdout en Bash


Quiero redireccionar stdout y stderr de un proceso a un solo archivo. ¿Cómo hago eso en Bash?


564
2018-03-12 09:14


origen


Respuestas:


Echar un vistazo aquí. Debiera ser:

yourcommand &>filename

(redirige ambos stdout y stderr al nombre del archivo).


661
2018-03-12 09:17



do_something 2>&1 | tee -a some_file

Esto va a redirigir stderr a stdout y stdout a some_file  y imprímalo en stdout.


393
2018-03-12 09:16



Puedes redirigir stderr a stdout y el stdout en 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 podría interpretarse como ejecutar el comando en segundo plano. También el formato es más legible 2 (es STDERR) redirigido a 1 (STDOUT).

EDITAR: cambió el orden como se señala en los comentarios


201
2017-12-13 10:30



# 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 redireccionar hacia adentro / hacia afuera de un comando al que llama desde su script, las respuestas ya están dadas. El mío es sobre redirigir dentro script actual que afecta a todos los comandos / built-ins (incluye forks) después del fragmento de código mencionado.


Otra buena solución es redirigir a STD-err / out Y al registrador o al archivo de registro de una vez, lo que implica dividir "una secuencia" en dos. Esta funcionalidad es provista por el comando 'tee' que puede escribir / agregar a varios descriptores de archivos (archivos, tomas de corriente, 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 un terminal conectado a / dev / stdout (FD # 1) y / dev / stderr (FD # 2). En la práctica, podría ser una tubería, zócalo o lo que sea.

  • Crea FDs # 3 y # 4 y apunta a la misma "ubicación" como # 1 y # 2 respectivamente. Cambiar FD # 1 no afecta FD # 3 a partir de ahora. Ahora, los FDs # 3 y # 4 apuntan a STDOUT y STDERR respectivamente. Estos serán usados ​​como real terminal STDOUT y STDERR.
  • 1>> (...) redirige STDOUT al comando en parens
  • parens (sub-shell) ejecuta la lectura 'tee' del STDOUT (canal) del ejecutivo y redirecciona al comando 'logger' a través de otro conducto al subconjunto en parens. Al mismo tiempo, copia la misma entrada a FD # 3 (terminal)
  • la segunda parte, muy similar, trata sobre hacer el mismo truco para STDERR y FDs # 2 y # 4.

El resultado de ejecutar un script con la línea anterior y, además, este:

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 guión:

ls -l /proc/self/fd/
ps xf

171
2018-03-12 09:17



bash your_script.sh 1>file.log 2>&1

1>file.log instruye al shell para que envíe STDOUT al archivo file.logy 2>&1 le dice que redirija STDERR (descriptor de archivo 2) a STDOUT (descriptor de archivo 1).

Nota: El orden importa como lo señaló liw.fi, 2>&1 1>file.log no funciona


34
2018-05-06 14:12



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

20
2017-11-19 01:55



Respuesta corta: Command >filename 2>&1 o Command &>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 dejamos fuera el '&', este comando se imprimiría stdout a stdout, y crea un archivo llamado "2" y escribe stderror ahí.

Al experimentar con el código anterior, puede ver exactamente cómo funcionan los operadores de redirección. Por ejemplo, cambiando qué archivo de los dos descriptores 1,2, se redirige a /dev/null las siguientes dos líneas de código eliminan todo desde stdout y todo desde 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 la cual el siguiente código no produce ningún resultado:

(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1

Para entender esto realmente, te recomiendo que leas esto página web en tablas de descriptores de archivos. Suponiendo que hayas hecho esa lectura, podemos proceder. Tenga en cuenta que Bash procesa de izquierda a derecha; así Bash ve >/dev/null primero (que es lo mismo que 1>/dev/null), y configura el descriptor de archivo 1 para que apunte a / dev / null en lugar de stdout. Habiendo hecho esto, Bash luego se mueve hacia la derecha y ve 2>&1. Esto establece el descriptor de archivo 2 apuntar al mismo archivo como descriptor de archivo 1 (y no para el descriptor de archivo 1 mismo !!!! este recurso en punteros para 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 que no se procesa ningún resultado.


Para probar si realmente entiende el concepto, trate de 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> y 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. A continuación, establece el descriptor de archivo 1 (recuerde que> / dev / null = 1> / dev / null) para que apunte a> / dev / null, eliminando así todo lo que normalmente se enviaría a la salida estándar. Por lo tanto, todo lo que queda es lo que no se envió a stdout en la subcadena (el código entre paréntesis), es decir, "stderror".   Lo interesante a tener en cuenta es que aunque 1 es solo un puntero al stdout, redirecciona el 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ódigo 2>&1 >/dev/null le daría a la cadena del apuntador 2 -> 1 -> / dev / null, y así el código no generaría nada, en contraste con lo que vimos arriba.


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 las salidas 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

Puntos clave:

  • Los descriptores de archivo se comportan como punteros (aunque los descriptores de archivo no son lo mismo que los punteros de archivo)
  • Al redirigir un descriptor de archivo "a" a un descriptor de archivo "b" que apunta al archivo "f", el descriptor de archivo "a" apunta al mismo lugar que el descriptor de archivo b - archivo "f". NO forma una cadena de punteros a -> b -> f
  • Por lo anterior, el orden importa, 2>&1 >/dev/null es! = >/dev/null 2>&1. ¡Uno genera salida y el otro no!

Por último, eche un vistazo a estos excelentes recursos:

Bash Documentation on Redirection, Una explicación de tablas de descriptores de archivos, Introducción a los indicadores


9
2018-04-23 13:14



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 y stderr en syslog.

Casi funciona, pero no de xinted; (


8
2018-01-19 19:21



Quería una solución para tener la salida de stdout plus stderr escrita en un archivo de registro y stderr todavía en la consola. Así que necesitaba duplicar la salida de 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
  • Primer swder stderr y stdout
  • a continuación, agregue el stdout al archivo de registro
  • pipe stderr a tee y añádalo también al archivo de registro

5
2018-02-04 13:57



La manera "más fácil" (solo bash4): ls * 2>&- 1>&-.


1
2018-04-23 05:07



Para tcsh, tengo que usar el siguiente comando:

command >& file

Si uso command &> file , dará un error de "Comando nulo inválido".


0
2017-07-26 15:53