resueltos - Cómo almacenar el error estándar en una variable en un script Bash
scripts linux ejercicios resueltos (12)
Digamos que tengo un script como el siguiente:
inútil.sh
echo "This Is Error" 1>&2
echo "This Is Output"
Y tengo otro script de shell:
alsoUseless.sh
./useless.sh | sed ''s/Output/Useless/''
Quiero capturar "This Is Error", o cualquier otra stderr de inútil.sh, en una variable. Vamos a llamarlo ERROR.
Tenga en cuenta que estoy usando stdout para algo. Quiero continuar usando stdout, por lo que redirigir stderr a stdout no es útil, en este caso.
Entonces, básicamente, quiero hacer
./useless.sh 2> $ERROR | ...
pero eso obviamente no funciona.
También sé que podría hacer
./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`
pero eso es feo e innecesario.
Lamentablemente, si no aparece ninguna respuesta aquí, eso es lo que voy a tener que hacer.
Espero que haya otra manera.
¿Alguien tiene mejores ideas?
alsoUseless.sh
Esto le permitirá canalizar la salida de su script useless.sh
través de un comando como sed
y guardar el stderr
en una variable llamada error
. El resultado de la tubería se envía a stdout
para su visualización o para ser canalizado a otro comando.
Configura un par de descriptores de archivos adicionales para gestionar las redirecciones necesarias para hacer esto.
#!/bin/bash
exec 3>&1 4>&2 #set up extra file descriptors
error=$( { ./useless.sh | sed ''s/Output/Useless/'' 2>&4 1>&3; } 2>&1 )
echo "The message is /"${error}./""
exec 3>&- 4>&- # release the extra file descriptors
Así es como lo hice:
#
# $1 - name of the (global) variable where the contents of stderr will be stored
# $2 - command to be executed
#
captureStderr()
{
local tmpFile=$(mktemp)
$2 2> $tmpFile
eval "$1=$(< $tmpFile)"
rm $tmpFile
}
Ejemplo de uso:
captureStderr err "./useless.sh"
echo -$err-
Utiliza un archivo temporal. Pero al menos las cosas feas están envueltas en una función.
En zsh:
{ . ./useless.sh > /dev/tty } 2>&1 | read ERROR
$ echo $ERROR
( your message )
Esta publicación me ayudó a encontrar una solución similar para mis propios fines:
MESSAGE=`{ echo $ERROR_MESSAGE | format_logs.py --level=ERROR; } 2>&1`
Entonces, siempre y cuando nuestro MENSAJE no sea una cadena vacía, lo transmitiremos a otras cosas. Esto nos permitirá saber si nuestro format_logs.py falló con algún tipo de excepción de python.
Este es un problema interesante al que esperaba que hubiera una solución elegante. Tristemente, termino con una solución similar a la del Sr. Leffler, pero agregaré que puedes llamar inútil desde dentro una función Bash para una mejor legibilidad:
#!/bin/bash function useless { /tmp/useless.sh | sed ''s/Output/Useless/'' } ERROR=$(useless) echo $ERROR
Todo otro tipo de redirección de salida debe estar respaldado por un archivo temporal.
Hay muchos duplicados para esta pregunta, muchos de los cuales tienen un escenario de uso ligeramente más simple en el que no desea capturar stderr y stdout y el código de salida al mismo tiempo.
if result=$(useless.sh 2>&1); then
stdout=$result
else
rc=$?
stderr=$result
fi
funciona para el escenario común en el que se espera una salida adecuada en caso de éxito o un mensaje de diagnóstico en stderr en caso de fallo.
Tenga en cuenta que las sentencias de control de shell ya examinan $?
bajo el capó; así que cualquier cosa que se vea
cmd
if [ $? -eq 0 ], then ...
es solo una forma torpe y unidiomática de decir
if cmd; then ...
Para corregir errores en tus comandos:
execute [INVOKING-FUNCTION] [COMMAND]
execute () {
error=$($2 2>&1 >/dev/null)
if [ $? -ne 0 ]; then
echo "$1: $error"
exit 1
fi
}
Inspirado en la fabricación ajustada:
Redirigido stderr a stdout, stdout a / dev / null, y luego use los backticks o $()
para capturar el stderr redirigido:
ERROR=$(./useless.sh 2>&1 >/dev/null)
Sería mejor capturar el archivo de error así:
ERROR=$(</tmp/Error)
El shell lo reconoce y no tiene que ejecutar '' cat
'' para obtener los datos.
La gran pregunta es difícil. No creo que haya una manera fácil de hacerlo. Tendría que construir toda la tubería en el subconjunto, enviando finalmente su salida estándar final a un archivo, para que pueda redirigir los errores al resultado estándar.
ERROR=$( { ./useless.sh | sed s/Output/Useless/ > outfile; } 2>&1 )
Tenga en cuenta que se necesita el punto y coma (en conchas clásicas, Bourne, Korn, seguro, también en Bash). El '' {}
'' hace la redirección de E / S sobre los comandos incluidos. Como está escrito, también capturaría errores de sed
.
(Código formalmente no probado: uso bajo su propio riesgo).
Si desea omitir el uso de un archivo temporal, puede usar la sustitución de procesos. Todavía no he conseguido que funcione. Este fue mi primer intento:
$ .useless.sh 2> >( ERROR=$(<) )
-bash: command substitution: line 42: syntax error near unexpected token `)''
-bash: command substitution: line 42: `<)''
Entonces probé
$ ./useless.sh 2> >( ERROR=$( cat <() ) )
This Is Output
$ echo $ERROR # $ERROR is empty
sin embargo
$ ./useless.sh 2> >( cat <() > asdf.txt )
This Is Output
$ cat asdf.txt
This Is Error
Entonces la sustitución del proceso generalmente está haciendo lo correcto ... desafortunadamente, siempre que envuelvo STDIN dentro de >( )
con algo en $()
en un intento de capturar eso a una variable, pierdo el contenido de $()
. Creo que esto se debe a que $()
inicia un subproceso que ya no tiene acceso al descriptor de archivo en / dev / fd que es propiedad del proceso principal.
La sustitución de procesos me ha dado la capacidad de trabajar con un flujo de datos que ya no está en STDERR. Desafortunadamente, no parece poder manipularlo como quiero.
# command receives its input from stdin.
# command sends its output to stdout.
exec 3>&1
stderr="$(command </dev/stdin 2>&1 1>&3)"
exitcode="${?}"
echo "STDERR: $stderr"
exit ${exitcode}
$ b=$( ( a=$( (echo stdout;echo stderr >&2) ) ) 2>&1 )
$ echo "a=>$a b=>$b"
a=>stdout b=>stderr