Issue
I am trying to understand compound lists in the bash environment and what the boolean return value is at the end of the list, but I have not found a complete answer.
The definition of compound lists given here gives an example of a compound list in a while loop:
while
# a couple of <newline>s
# a list
date && who || ls; cat file
# a couple of <newline>s
# another list
wc file > output & true
do
# 2 lists
ls
cat file
done
But I don't quite understand the conditional - does the while loop only execute if all conditions in the list are true? or is it only true if the last condition is true?
A simpler example might be the following:
if
condition1
condition2
then
command1
fi
Does command1
execute only if condition2 is true or do both condition1 && condition2 have to be true in order to execute, i.e.:
if
condition1 && \
condition2
then
command1
fi
Solution
That code is equivalent to:
while date && who || ls; cat file; wc file > output & true
do
ls
cat file
done
which would be equivalent to:
foo() {
date; rc=$?
if (( rc == 0 ))
then
who; rc=$?
fi
if (( rc != 0 ))
then
ls
fi
cat file
wc file > output &
true
}
while foo
do
ls
cat file
done
So, given a semi-colon or newline separated list of commands, the final result is entirely the result of the final command in the list and so that function foo()
will always return an exit status of success
since the last command in it is true
and that's just what true
does.
Time for a quick segue, I think, to address your use of the word "condition" in your question. In C or many other languages when you write:
if ( foo ) { do stuff }
foo
is indeed a condition but in shell when you write:
if foo; then do stuff; fi
foo
is not a condition, it's a command (or list of commands) that if
executes and then tests the exit status (still not a condition) from the last command it ran. If that exit status is success
(0
) then the part after then
is executed, otherwise it's failure
(non-zero) and so the part after then
is not executed.
You could consider the "condition" in that context to be that the exit status was success
but neither the command nor it's exit status is itself a "condition". The same applies to while
and other "conditional constructs" - the thing that comes after if
or while
is a command, not a condition, and what is tested is the exit status of the command, also not a condition. The result of testing that exit status for success is the condition.
A good example is the command true
- that is not a condition, it's a command whose only job is to generate the exit status success
so you can use it in a conditional context like if true; then
or while true do
.
You provided these other scripts as examples:
1)
if
condition1
condition2
then
command1
fi
if
condition1 && \
condition2
then
command1
fi
but the word condition
in those is misleading. What they REALLY are is:
1)
if
command1
command2
then
command3
fi
if
command1 && \
command2
then
command3
fi
That first one:
if
command1
command2
then
command3
fi
says execute command1
and discard it's exit status, then execute command2
and if that exits with status success
then execute command3
.
The second one:
if
command1 && \
command2
then
command3
fi
says execute command1
and if that exits with status success
then execute command2
and if that also exits with status success
then execute command3
.
The only really tricky thing in the &&
and ||
operators is the tendency to think of them as ternary operators when they aren't. If:
a && b || c
were a ternary expression then it'd mean:
if a succeeded then
b
else
c
fi
so a
will execute followed by either b
or c
but never both, but it doesn't mean that in this context, it means:
if a succeeded then
b
fi
if a or b failed then
c
fi
so a
will execute followed by either b
or c
or (b
then c
if b
failed).
One final related bit of information - when you write a C function like:
int foo() {
printf("bar\n");
return 7;
}
and call it as:
tmp=foo();
you will find that bar
(the output) is written to stdout
while 7
(the return code) is stored in tmp
.
If you write similar-looking shell:
foo() {
printf "bar\n"
return 7
}
tmp=$(foo)
you will find that bar
is stored in tmp
while 7
is stored in the builtin variable $?
(which holds the exit status of the last command ran) and nothing is written to stdout
. So just keep in mind that:
return
actually sets the exit status for a function, just likeexit
sets it for a script.var=$(command)
stores the output of the command (including functions) invar
, not the exit status set byreturn
orexit
.- The exit status set by
return
from a function orexit
from a script is stored in$?
.
That is why in the code I wrote at the start of this answer you will see:
date; rc=$?
if (( rc == 0 ))
then
I'm saving the exit status from date
in a variable (because I also need it again later in the script) and then if
is executing the arithmetic command ((...))
which is then comparing the contents of rc
to the number 0
(the success
exit status) and if they match then that arithmetic command exits with status success
and then whatever command comes after then
gets executed.
Hope that helps and I hope the discussion of commands, exit status (success or fail) and conditions (true or false) above makes things a bit clearer and doesn't add to your confusion.
Answered By - Ed Morton Answer Checked By - Marilyn (WPSolving Volunteer)