
Quiero capturar el estado de salida de un comando que tiene lugar en algún lugar de una tubería.antesla última posición. Por ejemplo, si la tubería es algo así como
command_1 ... | command_2 ... | command_3 ... | ... | command_n
...Me gustaría saber cómo capturar el estado de salida de command_1
, o de command_2
, o de command_3
, etc. (Capturar el estado de salida de command_n
es trivial, por supuesto).
Además, en caso de que sea importante, esta canalización se produce dentro de una función de shell zsh.
Intenté capturar el estado de salida command_1
con algo como
function_with_pipeline () {
local command_1_status=-999999 # sentinel value
{ command_1 ...; command_1_status=$? } | command_2 ... | ... | command_n
...
}
...pero después de ejecutar la canalización, el valor de la command_1_status
variable seguía siendo el valor centinela.
FWIW, aquí hay un ejemplo práctico, donde la canalización tiene solo dos comandos:
foo ... | grep ...
foo
es una función definida para este ejemplo, así:
foo () {
(( $1 & 1 )) && echo "a non-neglible message"
(( $1 & 2 )) && echo "a negligible message"
(( $1 & 4 )) && echo "error message" >&2
return $(( ( $1 & 4 ) >> 2 ))
}
El objetivo es capturar el estado de salida de la llamada foo
en el proceso.
La función function_with_pipeline
implementa la estrategia (en última instancia ineficaz) que describí anteriormente para hacer esto:
function_with_pipeline () {
local foo_status=-999999 # sentinel value
{ foo $1; foo_status=$? } | grep -v "a negligible message"
printf '%d\ndesired: %d; actual: %d\n\n' $1 $(( ( $1 & 4 ) >> 2 )) $foo_status
}
El siguiente bucle ejercita la function_with_pipeline
función. El resultado muestra que el valor de la variable local foo_status
no termina siendo diferente de cómo comenzó.
for i in $(seq 0 7)
do
function_with_pipeline $i
done
# 0
# desired: 0; actual: -999999
#
# a non-neglible message
# 1
# desired: 0; actual: -999999
#
# 2
# desired: 0; actual: -999999
#
# a non-neglible message
# 3
# desired: 0; actual: -999999
#
# error message
# 4
# desired: 1; actual: -999999
#
# error message
# a non-neglible message
# 5
# desired: 1; actual: -999999
#
# error message
# 6
# desired: 1; actual: -999999
#
# error message
# a non-neglible message
# 7
# desired: 1; actual: -999999
#
Obtengo los mismos resultados si omito la local
declaración en la definición de foo_status
.
Respuesta1
Hay una matriz especial pipestatus
para eso zsh
, así que intenta
command_1 ... | command_2 ... | command_3
y
echo $pipestatus[1] $pipestatus[2] $pipestatus[3]
y la razón por la que su enfoque no funciona es porque cada tubería se ejecuta en un subnivel separado, con sus propias variables que se destruyen una vez que sale del subnivel.
Sólo como referencia, está PIPESTATUS
(con letras mayúsculas) en bash
.
Respuesta2
mispipe
Funciona en cualquier caparazón. La sintaxis (en comparación con una tubería normal) funciona así:
mispipe true false ; echo $? # returns exit code of 1st command `true`
true | false ; echo $? # returns exit code of 2nd command `false`
Producción:
0
1
Qué hacer si hay más de dos programas:
# this still returns exit code of 1st command `true`
mispipe true 'false | false | false' ; echo $?
Producción:
0
A pesar de la falta de un visible |
, todavía se comporta como debería hacerlo una tubería:
yes | mispipe head 'wc -c'
Producción:
20