Как зафиксировать статус завершения нефинальной команды в конвейере?

Как зафиксировать статус завершения нефинальной команды в конвейере?

Я хочу зафиксировать статус завершения команды, которая выполняется где-то в конвейере.допоследняя позиция. Например, если трубопровод что-то вроде

command_1 ... | command_2 ... | command_3 ... | ... | command_n

...Я хотел бы знать, как захватить статус выхода command_1, или command_2, или command_3и т. д. (Захват статуса выхода command_n, конечно, тривиален.)

Также, если это имеет значение, этот конвейер выполняется внутри функции оболочки zsh.


Я попытался зафиксировать статус выхода command_1с помощью чего-то вроде

function_with_pipeline () {

    local command_1_status=-999999  # sentinel value

    { command_1 ...; command_1_status=$? } | command_2 ... | ... | command_n
    ...

}

...но после запуска конвейера значение переменной command_1_statusвсе еще оставалось контрольным значением.


Кстати, вот рабочий пример, где конвейер имеет всего две команды:

foo ... | grep ...

foo— это функция, определенная для этого примера, например:

foo () {

    (( $1 & 1 )) && echo "a non-neglible message"
    (( $1 & 2 )) && echo "a negligible message"
    (( $1 & 4 )) && echo "error message" >&2

    return $(( ( $1 & 4 ) >> 2 ))
}

Цель состоит в том, чтобы зафиксировать статус выхода вызова fooв конвейере.

Для этого функция function_with_pipelineреализует (в конечном итоге неэффективную) стратегию, которую я описал выше:

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

}

Цикл ниже выполняет function_with_pipelineфункцию. Выход показывает, что значение локальной переменной foo_statusв итоге не отличается от того, каким оно было в начале.

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
#

Я получу тот же результат, если опущу localобъявление в определении foo_status.

решение1

pipestatusДля этого есть специальный массив zsh, так что попробуйте

command_1 ... | command_2 ... | command_3

и

echo $pipestatus[1] $pipestatus[2] $pipestatus[3]

и причина, по которой ваш подход не работает, заключается в том, что каждый канал выполняется в отдельной подоболочке со своими собственными переменными, которые уничтожаются после выхода из подоболочки.


Для справки, это PIPESTATUS(заглавными буквами) в формате bash.

решение2

mispipeработает в любой оболочке. Синтаксис (по сравнению с обычным конвейером) работает так:

mispipe true false ; echo $?  # returns exit code of 1st command `true`
true | false ; echo $?  # returns exit code of 2nd command `false`

Выход:

0
1

Что делать, если программ больше двух:

# this still returns exit code of 1st command `true`
mispipe true 'false | false | false' ; echo $?

Выход:

0

Несмотря на отсутствие видимого |, он все равно ведет себя так, как и положено трубе:

yes | mispipe head 'wc -c'

Выход:

     20

Связанный контент