skip to Main Content

Given the following simple snippet, saved in a file named test.sh:

sleep 3 && exit 50&
echo "Wait for ${!} for the first time."
wait "${!}"
echo "Exit code is: ${?}"
sleep 1     # this line makes a difference
echo "Wait for ${!} for the second time."
wait "${!}"
echo "Exit code is: ${?}"

Running the file using bash test.sh gives the following output:

Wait for 40781 for the first time.
Exit code is: 50
Wait for 40781 for the second time.
Exit code is: 50

While running the file using sh test.sh (I tried on both Ubuntu dash and BusyBox ash) gives the following output:

Wait for 40773 for the first time.
Exit code is: 50
Wait for 40773 for the second time.
Exit code is: 127

In ash, all commands put between 2 calls to wait "${!}" make the exit code change to 127, with the exception of echo command. Also, with the sleep 1 line removed, running the file using sh test.sh gives exit code 50, but manually copying all lines and pasting to the sh terminal gives 127 again.

I really don’t understand this behavior, and it causes me quite a lot of headache debugging my code. Can someone explain?

2

Answers


  1. According to this, the $! variable represents "The PID of the most recent background command". In the example above, there is one and only one background command:

    sleep 3 && exit 50& # <-- "&" sends to background
    

    The second call to sleep is not executed as a background command.

    sleep 1 # <-- This is missing the "&" to send it to the background
    

    The difference in behavior is with the wait command, which is a shell built-in. Shell built-ins can and will change in behavior between different shell implementations.

    • In bash, when you ask wait to wait for a process that’s already terminated, it gives the last status, which is 50 as set earlier.
    • In sh (as well as ash, ksh) when you ask wait to wait for a process that’s already terminated, it errors with test.sh: line 9: wait: pid 27016 is not a child of this shell which is 127.
    Login or Signup to reply.
  2. bash behaves the same way as busybox sh/dash in POSIX mode,

    $ set -o posix
    $ sleep 3 && exit 42 &
    [1] 8573
    $ wait $!; echo $?
    [1]+  Done(42)                sleep 3 && exit 42
    42
    $ wait $!; echo $?
    bash: wait: pid 8573 is not a child of this shell
    127
    

    and this is mentioned in the official bash POSIX mode description as follows:

    1. Bash removes an exited background process’s status from the list
      of such statuses after the ‘wait’ builtin is used to obtain it.

    And wrt

    In ash, all commands put between 2 calls to wait "${!}" make the exit code change to 127, with the exception of echo command.

    this looks like a bug that is fixed in NetBSD sh and FreeBSD sh but not in busybox sh and dash.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search