Of course "spawning a shell" can be very cheap, as you are already in a shell, it's just fork() plus some initialization code (compared to fork() plus exec() for running a non-shell program)
Note that shells must try to interpret at least any text file for which the system returns ENOEXEC [1]
1: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3... Read subsection 1.e.i.b
Files:
1.sh:
true
2.sh: #!/bin/bash
true
test1.sh: for i in `seq 10000`
do
./1.sh
done
test2.sh: for i in `seq 10000`
do
./2.sh
done
Times for test1.sh: 11.484s
11.590s
11.405s
Times for test2.sh: 14.273s
14.391s
13.847s
So including the #! adds about 23% to the overhead of calling a shell script. Tested on Mint 19.1, bash.EDIT: for reference, sourcing the scripts instead takes an average of 0.0842s for test1 and 0897s for test2. (and all test2 trials were still slower than all test1 trials), which isn't particuarly suprising.
Inlining true takes about 0.029s
Besides, /bin/sh would also require dereferencing a symlink (to /bin/dash), which also doesn't seem fair.
/proc/bin/true
/proc/bin/false
The exec doesn't even need to create page tables or run any non-kernel code. This is a benchmark-winning design.