Normal shell vs subshell vs "bash -c" last command exit code

Solution 1:

Why do I get exit code 0 here?

The reason is that with the double quotes, parameter expansion (of the special parameter, $?) is carried out before passing arguments to the new Bash process. If you turn on debug and verbose mode, you can see this clearly:

$ set -xv
$ bash -c "(./exit-code.sh 1 && ./exit-code.sh 2 && ./exit-code.sh 3 || echo last exit code: $?)"
bash -c "(./exit-code.sh 1 && ./exit-code.sh 2 && ./exit-code.sh 3 || echo last exit code: $?)"
+ bash -c '(./exit-code.sh 1 && ./exit-code.sh 2 && ./exit-code.sh 3 || echo last exit code: 0)'
running exit-code with 1
last exit code: 0
$ set +xv

In this case $? is set to 0 because the previous command (set -xv in this example) executed successfully.

Solution 2:

Quotes

Just use single ' quotes

/bin/bash -c './exit-code.sh 1 && ./exit-code.sh 2 && ./exit-code.sh 3 || echo last exit code: $?'

running exit-code with 1
last exit code: 1