Digamos que tengo un script como el siguiente:
useless.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 otro stderr de useless.sh, en una variable. Llamémoslo ERROR.
Tenga en cuenta que estoy usando stdout para algo. Quiero seguir 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.
Desafortunadamente, si no aparecen respuestas aquí, eso es lo que voy a tener que hacer.
Espero que haya otra forma.
Alguien tiene alguna idea mejor?
ERROR=$(./useless.sh | sed 's/Output/Useless/' 2>&1 1>/dev/ttyX)
Respuestas:
Sería mejor capturar el archivo de error así:
El shell reconoce esto y no tiene que ejecutarse '
cat
' para obtener los datos.La pregunta más grande es difícil. No creo que haya una manera fácil de hacerlo. Tendría que construir toda la canalización en el subconjunto, eventualmente enviando su salida estándar final a un archivo, para que pueda redirigir los errores a la salida estándar.
Tenga en cuenta que el punto y coma es necesario (en conchas clásicas, Bourne, Korn, seguro; probablemente también en Bash). El '
{}
' realiza la redirección de E / S sobre los comandos adjuntos. Tal como está escrito, también capturaría erroressed
.fuente
/dev/null
lugar deoutfile
(si es como yo, encontró esta pregunta a través de Google y no tiene los mismos requisitos que el OP)stdout
ystderr
adelante y atrás. Pero cuidado , como se dice aquí : en bash, sería mejor no asumir que el descriptor de archivo 3 no sealsoUseless.sh
Esto le permitirá canalizar la salida de su
useless.sh
script a través de un comando comosed
y guardarlostderr
en una variable llamadaerror
. El resultado de la tubería se envíastdout
para su visualización o para ser canalizado a otro comando.Configura un par de descriptores de archivo adicionales para administrar las redirecciones necesarias para hacer esto.
fuente
stderr
ystdout
en variables?dry_run
función que puede elegir de manera confiable entre hacer eco de sus argumentos y ejecutarlos, independientemente de si el comando que se ejecuta en seco se canaliza a otro archivo.read
no acepta entradas de una tubería. Puede usar otras técnicas para lograr lo que está tratando de demostrar.Stderr redirigido a stdout, stdout a / dev / null, y luego use los backticks o
$()
para capturar el stderr redirigido:fuente
PY_VERSION="$(python --version 2>&1)"
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, todo al mismo tiempo.
funciona para el escenario común donde se espera una salida adecuada en caso de éxito o un mensaje de diagnóstico en stderr en caso de falla.
Tenga en cuenta que las declaraciones de control del shell ya se examinan
$?
bajo el capó; así que todo lo que parecees solo una forma torpe y unidiomática de decir
fuente
fuente
command
es una mala elección aquí, ya que en realidad hay un nombre incorporado con ese nombre. Podría hacerloyourCommand
o algo así, para ser más explícito.Para beneficio del lector, esta receta aquí
Si desea captura
stderr
de algunoscommand
envar
que puede hacerDespués lo tienes todo:
Si
command
es simple (no algo asía | b
), puede dejar el interior{}
alejado:Envuelto en una función fácil de reutilizar
bash
(probablemente necesita la versión 3 y superior paralocal -n
):Explicado:
local -n
alias "$ 1" (que es la variable paracatch-stderr
)3>&1
usa el descriptor de archivo 3 para guardar sus puntos estándar{ command; }
(o "$ @") luego ejecuta el comando dentro de la captura de salida$(..)
2>&1
redirigestderr
a la captura de salida$(..)
1>&3
redirigestdout
lejos de la captura de salida de$(..)
nuevo al "exterior"stdout
que se guardó en el descriptor de archivo 3. Tenga en cuenta questderr
todavía se refiere a donde FD 1 señaló antes: a la captura de salida$(..)
3>&-
luego cierra el descriptor de archivo 3 ya que ya no es necesario, de modo quecommand
no aparece repentinamente un descriptor de archivo abierto desconocido. Tenga en cuenta que la carcasa externa todavía tiene FD 3 abierto, perocommand
no lo verá.lvm
quejarse de descriptores de archivos inesperados. Y selvm
quejastderr
, ¡justo lo que vamos a capturar!Puede capturar cualquier otro descriptor de archivo con esta receta, si se adapta en consecuencia. Excepto el descriptor de archivo 1, por supuesto (aquí la lógica de redireccionamiento sería incorrecta, pero para el descriptor de archivo 1 puede usar
var=$(command)
como de costumbre).Tenga en cuenta que esto sacrifica el descriptor de archivo 3. Si necesita ese descriptor de archivo, no dude en cambiar el número. Pero tenga en cuenta que algunos proyectiles (de la década de 1980) podrían entenderse
99>&1
como un argumento9
seguido de9>&1
(esto no es un problema parabash
).También tenga en cuenta que no es particularmente fácil hacer que este FD 3 sea configurable a través de una variable. Esto hace que las cosas sean muy ilegibles:
Notas:
catch-var-from-fd-by-fd var 2 3 cmd..
es lo mismo quecatch-stderr var cmd..
shift || return
es solo una forma de evitar errores feos en caso de que olvide dar el número correcto de argumentos. Quizás terminar el shell sería otra forma (pero esto dificulta la prueba desde la línea de comandos).exec
, pero luego se vuelve realmente fea.bash
tan bien que no hay necesidad de hacerlolocal -n
. Sin embargo, entonces no puedes usar variables locales y ¡se vuelve extremadamente feo!eval
s se utilizan de manera segura. Poreval
lo general, se considera peligroso. Sin embargo, en este caso no es más malvado que usar"$@"
(para ejecutar comandos arbitrarios). Sin embargo, asegúrese de utilizar la cita exacta y correcta como se muestra aquí (de lo contrario, se vuelve muy muy peligroso ).fuente
Así es como lo hice:
Ejemplo de uso:
Se hace uso de un archivo temporal. Pero al menos las cosas feas están envueltas en una función.
fuente
eval
. Por ejemplo,printf -v "$1" '%s' "$(<tmpFile)"
no corre el riesgo de ejecutar código arbitrario si suTMPDIR
variable se ha establecido en un valor malicioso (o si el nombre de la variable de destino contiene dicho valor).rm -- "$tmpFile"
es más robusto querm $tmpFile
.Este es un problema interesante para el que esperaba que hubiera una solución elegante. Lamentablemente, termino con una solución similar al Sr. Leffler, pero agregaré que puede llamar inútil desde dentro de una función Bash para mejorar la legibilidad:
Todo otro tipo de redirección de salida debe estar respaldado por un archivo temporal.
fuente
POSIX
STDERR se puede capturar con algo de magia de redireccionamiento:
Tenga en cuenta que la canalización de STDOUT del comando (aquí
ls
) se realiza dentro del más interno{
}
. Si está ejecutando un comando simple (p. Ej., No una tubería), puede eliminar estas llaves internas.No puede canalizar fuera del comando, ya que la tubería crea una subshell en
bash
yzsh
, y la asignación a la variable en la subshell no estaría disponible para el shell actual.intento
En
bash
, sería mejor no asumir que el descriptor de archivo 3 no se utiliza:Tenga en cuenta que esto no funciona
zsh
.Gracias a esta respuesta por la idea general.
fuente
Esta publicación me ayudó a encontrar una solución similar para mis propios fines:
Entonces, siempre que nuestro MENSAJE no sea una cadena vacía, la pasamos a otras cosas. Esto nos permitirá saber si nuestro format_logs.py falló con algún tipo de excepción de Python.
fuente
Capturar e imprimir stderr
Descompostura
Puede usarlo
$()
para capturar stdout, pero desea capturar stderr en su lugar. Entonces intercambias stdout y stderr. Usar fd 3 como almacenamiento temporal en el algoritmo de intercambio estándar.Si desea capturar e imprimir, use
tee
para hacer un duplicado. En este caso, la salida detee
se capturará en$()
lugar de ir a la consola, pero stderr (oftee
) seguirá yendo a la consola, por lo que la usamos como la segunda salida atee
través del archivo especial,/dev/fd/2
ya quetee
espera una ruta de archivo en lugar de un fd número.NOTA: Esa es una gran cantidad de redireccionamientos en una sola línea y el orden es importante.
$()
está agarrando el stdout detee
al final de la tubería y la tubería en sí enruta stdout de./useless.sh
al stdin detee
DESPUÉS de que intercambiamos stdin y stdout por./useless.sh
.Usando stdout de ./useless.sh
El OP dijo que todavía quería usar (no solo imprimir) stdout, como
./useless.sh | sed 's/Output/Useless/'
.No hay problema, solo hágalo ANTES de intercambiar stdout y stderr. Recomiendo moverlo a una función o archivo (también-useless.sh) y llamarlo en lugar de ./useless.sh en la línea de arriba.
Sin embargo, si desea CAPTURAR stdout Y stderr, creo que debe recurrir a archivos temporales porque
$()
solo lo hará de uno en uno y creará una subshell desde la que no puede devolver variables.fuente
Iterando un poco la respuesta de Tom Hale, descubrí que es posible envolver el yoga de redirección en una función para una reutilización más fácil. Por ejemplo:
Es casi seguro que sea posible simplificar esto aún más. No lo he probado especialmente, pero parece funcionar tanto con bash como con ksh.
fuente
Si desea omitir el uso de un archivo temporal, puede usar la sustitución del proceso. Todavía no lo he conseguido para trabajar. Este fue mi primer intento:
Entonces intenté
sin embargo
Entonces, la sustitución del proceso generalmente está haciendo lo correcto ... desafortunadamente, cada vez que envuelvo STDIN dentro
>( )
con algo$()
en un intento de capturar eso en 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, propiedad del proceso padre.La sustitución de procesos me ha comprado la capacidad de trabajar con un flujo de datos que ya no está en STDERR, desafortunadamente no parece que pueda manipularlo de la manera que quiero.
fuente
./useless.sh 2> >( ERROR=$( cat <() ); echo "$ERROR" )
, vería la salida deERROR
. El problema es que la sustitución del proceso se ejecuta en un shell secundario, por lo que el valor establecido en el shell secundario no afecta al shell primario.fuente
a=> b=>stderr
a
se evalúa y se asigna en un sub-shell, y la asignación en el sub-shell no afecta al shell principal. (Probado en Ubuntu 14.04 LTS y Mac OS X 10.10.1.)GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)
)SLE 11.4
y produce el efecto descrito por @JonathanLefflerEn zsh:
fuente
Para errores de prueba de sus comandos:
Inspirado en la fabricación ajustada:
fuente
if
. Déjame publicar una solución por separado.Una solución simple
Producirá:
fuente
Mejorando la respuesta de YellowApple :
Esta es una función Bash para capturar stderr en cualquier variable
stderr_capture_example.sh
:Pruebas:
Salida:
Esta función se puede utilizar para capturar la elección devuelta de un
dialog
comando.fuente