world when simple hello for bash shell redirect pipe

when - Redirigir stderr y stdout en Bash



hello world bash (14)

@ fernando-fabreti

Agregando a 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"

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


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


Echa un vistazo here . Debiera ser:

yourcommand &>filename

(Redirige tanto stdout como stderr al nombre de archivo).


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


Las siguientes funciones se pueden usar 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"


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 (desde STDERR de "sort -c" o desde STDERR hasta "sort -h").


Para tcsh, tengo que usar el siguiente comando:

command >& file

Si usa el command &> file , aparecerá el error "Comando nulo no válido".


Puede redireccionar stderr a stdout y el stdout a un archivo:

some_command >file.log 2>&1

Consulte here

Este formato es preferible al formato más popular &> que solo funciona en bash. En el shell Bourne se podría interpretar que se ejecuta el comando en segundo plano. También el formato es más legible 2 (es STDERR) redirigido a 1 (STDOUT).

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


Quería una solución para tener el resultado de stdout plus stderr escrito en un archivo de registro y stderr aún en la consola. Así que necesitaba duplicar la salida stderr vía tee.

Esta es la solución que encontré:

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile

  • Primer intercambio de stderr y stdout.
  • a continuación, agregue la salida estándar al archivo de registro
  • tubo stderr a la T y anexarlo también al archivo de registro

Respuesta corta: Command >filename 2>&1 o Command &>filename

Explicación:

Considere el siguiente código que imprime la palabra "stdout" a stdout y la palabra "stderror" a 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 stderror el ''&'', este comando imprimirá stdout a stdout, 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 de cuál de los dos descriptores 1,2 se redirige a /dev/null las siguientes dos líneas de código eliminan todo del stdout, y todo del 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 que el siguiente código no produce resultados:

(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 proceder. 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 configura el descriptor de archivo 1 para que apunte a / dev / null en lugar de la salida estándar. 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 (consulte este recurso en los punteros para obtener 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 ninguna salida.

Para comprobar si realmente entiende el concepto, intente adivinar la salida cuando cambiemos el orden de redirección:

(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 así 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 lo que no se envió a la salida estándar en la subshell (el código entre paréntesis), es decir, "stderror". Lo interesante a tener en cuenta es que aunque 1 es solo un puntero a la salida estándar, redirigir el puntero 2 a 1 a través de 2>&1 NO forma una cadena de punteros 2 -> 1 -> stdout. Si lo hiciera, como resultado de redirigir 1 a / dev / null, el código 2>&1 >/dev/null daría la cadena de punteros 2 -> 1 -> / dev / null, y así el código no generaría nada, En contraste con lo que vimos arriba.

Finalmente, observaría que hay una forma más sencilla de hacer esto:

Desde la sección 3.6.4 here , vemos que podemos usar el operador &> para redireccionar 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

Puntos clave:

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

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

here , una explicación de las tablas de descriptores de archivos , Introducción a los punteros


# 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, el eco simple se escribirá en $ LOG_FILE. Útil para la demonización.

Al autor del post 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 es acerca de la redirección dentro del script actual que afecta a todos los comandos / incorporados (incluye las bifurcaciones) después del fragmento de código mencionado.

Otra solución interesante consiste en redirigir a std-err / out AND a logger o log file a la vez, lo que implica dividir "un flujo" en dos. Esta función es proporcionada por el comando ''tee'' que puede escribir / adjuntar a varios descriptores de archivos (archivos, sockets, 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 }

Así, desde el principio. Supongamos que tenemos el terminal conectado a / dev / stdout (FD # 1) y / dev / stderr (FD # 2). En la práctica, podría ser una tubería, un zócalo o lo que sea.

  • Cree los FD # 3 y # 4 y apunte a la misma "ubicación" como # 1 y # 2 respectivamente. Cambiar FD # 1 no afecta a FD # 3 a partir de ahora. Ahora, FDs # 3 y # 4 apuntan a STDOUT y STDERR respectivamente. Estos serán utilizados como terminal real STDOUT y STDERR.
  • 1>> (...) redirige STDOUT al comando en parens
  • parens (sub-shell) ejecuta la lectura ''tee'' de STDOUT (pipe) de exec y redirige al comando ''logger'' a través de otro pipe a sub-shell 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 script:

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


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;


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 indica que redirija STDERR (descriptor de archivo 2) a STDOUT (descriptor de archivo 1).

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


do_something 2>&1 | tee -a some_file

Esto va a redirigir stderr a stdout y stdout a some_file e imprimirlo a stdout.