¿Cómo capturar el estado de salida de un comando no final en una tubería?

¿Cómo capturar el estado de salida de un comando no final en una tubería?

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_nes 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_1con 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_statusvariable seguía siendo el valor centinela.


FWIW, aquí hay un ejemplo práctico, donde la canalización tiene solo dos comandos:

foo ... | grep ...

fooes 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 fooen el proceso.

La función function_with_pipelineimplementa 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_pipelinefunción. El resultado muestra que el valor de la variable local foo_statusno 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 localdeclaración en la definición de foo_status.

Respuesta1

Hay una matriz especial pipestatuspara 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

mispipeFunciona 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

información relacionada