A script without shebang is meant to be interpreted by a POSIX-compliant sh
interpreter. That's actually the POSIX way to write POSIX scripts, POSIX doesn't specify shebangs, though in practice using shebangs is more portable / reliable, and here is a good example why.
The bash shell is such a POSIX sh interpreter. bash (some versions and in some custom builds and in some environments) is actually the only FLOSS shell that I know that has been certified as compliant when running as sh
(not when running as bash
).
When executing a shebang-less script, bash, when execve()
returns ENOEXEC and after having checked that it doesn't look like a binary file, interprets it in a child of his, simulating an execution by attempting to reset its state to the default.
That means however that that script when run from bash
is interpreted as a bash script instead of a POSIX sh script unless bash
is running in POSIX mode itself (such as when invoked as sh
itself).
$ cat a
alias uname='echo hi'
uname
$ zsh -c ./a
hi
$ sh ./a
hi
$ bash -c ./a
Linux
$ (exec -a sh bash -c ./a)
hi
See how a
was interpreted as bash
language (ignoring aliases) instead of the sh languages when invoked by bash
.
~$ strace -qqfe execve bash -c ./a
execve("/usr/bin/bash", ["bash", "-c", "./a"], 0x7fff0081a820 /* 66 vars */) = 0
execve("./a", ["./a"], 0x55b18b3a4660 /* 66 vars */) = -1 ENOEXEC (Exec format error)
[pid 123559] execve("/usr/bin/uname", ["uname"], 0x55b18b3a4660 /* 66 vars */) = 0
Linux
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=123559, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
See how bash
didn't execute sh
to interpret the script.
The fact that you get that warning: execute_coproc: coproc [147878:COPROC] still exists
is a bug whereby bash
fails to reset its state properly.
In any case, coproc
is not a sh
keyword so doesn't have its place in a shebang-less script. coproc
is from zsh
(while coprocesses are from ksh), though bash
's implementation is completely different, so you should have a #! /bin/bash -
shebang here.
With bash ./inner.sh
or with a shebang, there is a proper execution of a new interpreter instance, and execve()
completely and correctly wipes the process memory.