Search code examples
bashwhile-loopscriptingzsh

Confused with `bash -c` command - does not work with global variables


I have two shell commands, actually the same snippet, when pasted them in the terminal:

This works:

while true; do \
sleep 1; \
ping -c 1 'www.google.com'; echo $?; \
if [ $? -eq 0 ]; then break; fi; \
done

this does not:

/bin/bash -c "(while true; do \
sleep 1; \
ping -c 1 'www.google.com'; \
if [ $? -eq 0 ]; then break; fi; \
done); \
exit 0"

This second command tells me that the exit status is 130 (in the line where the if performs) even if ping performs well so it exit status should be 0.

It's like the commands inside the string that I pass to /bin/bash -c do not register the last exit value inside the global variable $?. Is there any way to achieve that?

Thanks in advance


Solution

  • Your double quoted string is being expanded BEFORE the commands in the string are being interpreted by bash -c so that $? is likely a previous command's exit code.

    You can see this with an example where we also use the -x flag to see what's happening under the hood:

    $ set -x
    $ bash -c "v=5; echo $v"
    + bash -c 'v=3; echo '
    

    You can see that there is no value of $v to echo when execution hits that line as $v was already replaced before the command was interpreted by bash -c.

    For further expirementation; to get nearly the same output as your issue, do the following:

    1. Run cat with no parameters and ctrl+c to interupt.
    2. Run bash -c "echo 'hi'; echo $?"

    You should get hi and 130 as the output, which was the exit code of your sigint'd cat command. (note that it's not the exit 0 of the echo "hi" as you might expect).


    Thankfully for your command, you can just swap out your quotes to avoid this issue:

    /bin/bash -c '(while true; do \
    sleep 1; \
    ping -c 1 "www.google.com"; \
    if [ $? -eq 0 ]; then break; fi; \
    done); \
    exit 0'