GNU bash, version 5.2.15(1)-release (x86_64-pc-linux-gnu)
Consider this simple while loop:
while false; do true; done; echo "${PIPESTATUS[0]}"
It prints 1
instead of 0
. This is unexpected to me and I can't find any documentation about this.
The same loop, when checked for actual exit code:
while false; do true; done; echo $?
prints 0
, as expected.
I also noticed that if a break
is used in the body:
while true; do true; break; done; echo "${PIPESTATUS[0]}"
then both the PIPESTATUS
as well as the exit code are both 0
, as expected.
Setting pipefail
option does not seem to change this behaviour.
My guess: I guess the loop condition is technically the last statement to be executed, and hence its exit code is returned in PIPESTATUS
. This explains why break
did not return 1
while false
loop condition does. However, this leads to more questions:
PIPESTATUS
and actual exit code?while
loops end with PIPESTATUS
1
, since every loop has to stop due loop condition being false? (Except the loops that end due to a break
)break
?Thanks in advance for your help.
From 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.
So,
$PIPESTATUS
holds the result of the last forked command (false
in this case),$?
contains the status of the last script command (while ...;do ...;done
).Then
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'
More at this difference of $? and ${PIPESTATUS[0]} @ lists.gnu.org.
$?
is POSIX, while PIPESTATUS is a bash array...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")
Where $PIPESTATUS
hold result of all 3 commands ls
, wc
and 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
The group command if ... then ...(elif ... then ) ... (else ... then) ... fi
was executed successfully. His result code is 0
, but
ls
's result code was 2
,
wc
's result code was 0
,
cat
's result code was 1
and
sed
's result code was 4
.
until ls -ld / | wc | cat - | sed -n '/[a-z]/p' ; do echo Don't print this' done ; echo ${?@Q} ${PIPESTATUS[@]@A} $(( $? ${PIPESTATUS[@]/#/+} ))
'0' declare -a PIPESTATUS=([0]="0" [1]="0" [2]="0" [3]="0") 0
or with some cosmetic:
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
... But if not is a group command:
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