However, when it is run from the cron, it always outputs SHELL=/bin/sh. Why is this?
This is because cron set SHELL to /bin/sh as this is the default program cron uses to run crontab entries. From man 5 crontab:
Several environment variables are set up automatically by the cron(8) daemon. SHELL is set to /bin/sh...
And as already indicated by the answer of Evgeny Vereshchagin, that variable won't be modified by Bash later if it is already set.
How can I make cron apply the shebang?
Cron is already applying it for your own script, as you can see from the following example.
Let's say you have the following display_process_tree.sh:
#!/bin/bash pid=$$ depth=0 while [ "$pid" -gt 0 ] && read -r ppid name < <(ps -o ppid= -o comm= -p "$pid") do eval $(echo printf '" %0.s"' {0..$depth}) if [[ -r /proc/$pid/exe ]]; then echo -n "$name - " readlink -f /proc/$pid/exe else echo $name fi pid=$ppid depth=$((depth+1)) done
And a crontab like this:
* * * * * /path/to/display_process_tree.sh > /tmp/display_process_tree.log
Now, if you wait for the output to arrive to /tmp/display_process_tree.log, you will find a process tree like the following:
display_process - /bin/bash sh - /bin/dash cron cron systemd
That shows that cron actually used /bin/sh (linked to /bin/dash in my system) to open a new /bin/bash process for your script.
man 5 crontab. cron sets environment variables SHELL, LOGNAME, HOME and PATH.