GNU bash, version 5.2.15(1)-release (x86_64-pc-linux-gnu)
Рассмотрим этот простой цикл while:
while false; do true; done; echo "${PIPESTATUS[0]}"
Он печатает 1 вместо 0. Это неожиданно для меня, и я не могу найти никакой документации по этому поводу.
Тот же цикл при проверке фактического кода выхода:
while false; do true; done; echo $?
печатает 0, как и ожидалось.
Я также заметил, что если в теле используется break:
while true; do true; break; done; echo "${PIPESTATUS[0]}"
тогда и PIPESTATUS, и код выхода будут 0, как и ожидалось.
Установка опции pipefail, похоже, не меняет это поведение.
Мое предположение: я предполагаю, что условие цикла технически является последним оператором, который должен быть выполнен, и, следовательно, его код выхода возвращается в PIPESTATUS. Это объясняет, почему break не вернуло 1, а false условие цикла возвращает. Однако это приводит к еще большему количеству вопросов:
PIPESTATUS и фактическим кодом выхода?while не заканчиваются на PIPESTATUS1, поскольку каждый цикл должен предотвратить ложное условие цикла? (За исключением петель, которые заканчиваются break)break?Заранее спасибо за вашу помощь.





Из man bash:
man bash | grep -A3 PIPESTATUS PIPESTATUS An array variable (see Arrays below) containing a list of exit status values from the processes in the most-recently-executed foreground pipeline (which may contain only a single command).
man bash | sed -e '/^ *? /{N;q};d' ? Expands to the exit status of the most recently executed fore‐ ground pipeline.
Так,
$PIPESTATUS содержит результат последней раздвоенной команды (в данном случае false),$? содержит статус последней команды сценария (while ...;do ...;done).Затем
if ! :; then :;fi ; echo ${?@Q} ${PIPESTATUS[@]@Q}
'0' '0'
while ! :; do :;done ; echo ${?@Q} ${PIPESTATUS[@]@Q}
'0' '0'
if /bin/false; then :;fi ; echo ${?@Q} ${PIPESTATUS[@]@Q}
'0' '1'
while false; do :;done ; echo ${?@Q} ${PIPESTATUS[@]@Q}
'0' '1'
Подробнее об этой разнице в $? и ${PIPESTATUS[0]} @ lists.gnu.org.
$? — это POSIX, а PIPESTATUS — это массив bash...if ls /wrong/path | wc | sed 'w/wrong/path' >/dev/null ; then
echo Don't print this'
fi ; echo ${?@Q} ${PIPESTATUS[@]@A}
ls: cannot access '/wrong/path': No such file or directory
sed: couldn't open file /wrong/path: No such file or directory
'0' declare -a PIPESTATUS=([0] = "2" [1] = "0" [2] = "4")
Где $PIPESTATUS содержит результат всех трёх команд ls, wc и sed.
if ls /wrong/path | wc | cat - /wrong/path | sed 'w/wrong/path' >/dev/null ; then
echo Don't print this'
fi ; echo ${?@Q} ${PIPESTATUS[@]@A} $(( $? ${PIPESTATUS[@]/#/+} ))
ls: cannot access '/wrong/path': No such file or directory
cat: /wrong/path: No such file or directory
sed: couldn't open file /wrong/path: No such file or directory
'0' declare -a PIPESTATUS=([0] = "2" [1] = "0" [2] = "1" [3] = "4") 7
Групповая команда if ... then ...(elif ... then ) ... (else ... then) ... fi выполнена успешно. Его код результата — 0, но
Код результата ls был 2,
Код результата wc был 0,
Код результата cat был 1 и
Код результата sed был 4.
пока ls -ld / | туалет | кот - | sed -n '/[a-z]/p' ; делать эхо Не печатать это' сделанный ; echo ${?@Q} ${PIPESTATUS[@]@A} $(( $? ${PIPESTATUS[@]/#/+} ))
'0' declare -a PIPESTATUS=([0] = "0" [1] = "0" [2] = "0" [3] = "0") 0
или с какой-нибудь косметикой:
until ls -ld / | wc | cat - | sed -n '/[a-z]/p' ; do
echo Don't print this'
done; printf 'RC: $?=%s PIPESTATUS=(%s) Total=%d\n' \
$? "${PIPESTATUS[*]}" $(( $? ${PIPESTATUS[*]/#/+} ))
RC: $?=0 PIPESTATUS=(0 0 0 0) Total=0
while ls -ld /wrong/path | wc | cat /tmp/noperm - | sed -n '/[a-z]/w/wrong/path' ; do
echo Don't print this'
done ;printf 'RC: $?=%s PIPESTATUS=(%s) Total=%d\n' \
$? "${PIPESTATUS[*]}" $(( $? ${PIPESTATUS[*]/#/+} ))
ls: cannot access '/wrong/path': No such file or directory
cat: /tmp/noperm: Permission denied
sed: couldn't open file /wrong/path: No such file or directory
RC: $?=0 PIPESTATUS=(2 0 141 4) Total=147
... А если нет, то это групповая команда:
ls -ld /wrong/path | wc | cat /tmp/noperm - |
sed -n '/[a-z]/w/wrong/path'; printf 'RC: $?=%s PIPESTATUS=(%s) Total=%d\n' \
$? "${PIPESTATUS[*]}" $(( $? ${PIPESTATUS[*]/#/+} ))
ls: cannot access '/wrong/path': No such file or directory
sed: couldn't open file /wrong/path: No such file or directory
cat: /tmp/noperm: Permission denied
RC: $?=4 PIPESTATUS=(2 0 141 4) Total=151
+1 Ваше различие между форком и скриптом, кажется, объясняет это на 100%. Однако я до сих пор не могу найти никакой документации, подтверждающей это различие. В приведенных вами фрагментах man bash оба используют одну и ту же фразу «последний выполненный конвейер переднего плана».
$PIPESTATUSсодержит результат последней раздвоенной команды (в данном случаеfalse),$?содержит статус последней команды сценария (while ...;do ...;done).