2

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?

Livy
  • 631
  • 4
  • 15

2 Answers2

2

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.
tresf
  • 7,103
  • 6
  • 40
  • 101
  • I intentionally wait for the same process twice. The `sleep` command is there just to make the exit code change to 127 on `ash`. You can replace it with `cat` or anything to achieve the same effect. Also, I think you are not correct when you claim that `wait` on `ash` will fail if the child process already terminated. I have been waiting for terminated process just fine (you can try putting a `sleep 4` line after the `sleep 3&` -- I just don't understand why the second call to `wait` returns 127. – Livy Sep 15 '22 at 02:22
  • Hi, the example provided uses `bash` and `sh`. My claim about the process being gone is related to behavior with `sh`. The message about the process missing wasn't present on Ubuntu using `sh` or `ash`, but it did show when testing MacOS, which is where the above conclusion was drawn. The error is directly from the MacOS terminal, but is also available as an explanation here: https://stackoverflow.com/a/42545488/3196753 (`ksh`, but same behavior). That's identical behavior with 3 other shells, so `bash` really seems to be the outlier here. – tresf Sep 15 '22 at 02:49
  • Another useful answer: https://stackoverflow.com/a/71884901/3196753. I'm sharing this because I'm not sure if your intention in the original example was to send `sleep` to the background or not (as it stands, I believe it sends `exit` to the background). Regardless, in my tests `bash` sends the last background exit code whereas the other shells do not. – tresf Sep 15 '22 at 02:58
  • After some digging, I finally found someone else describing this: https://unix.stackexchange.com/a/456974/190347. Added to answer. – tresf Sep 15 '22 at 03:04
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.

oguz ismail
  • 1
  • 16
  • 47
  • 69