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