Shebang does not set SHELL in cron

I have a script containing:


When I run it from the command line:


every time, it outputs SHELL=/bin/bash. However, when it is run from the cron, it always outputs SHELL=/bin/sh. Why is this? How can I make cron apply the shebang?

I already checked the cron PATH; it does include /bin.

Asked By: Benubird


Bash Reference Manual says:

SHELL – The full pathname to the shell is kept in this environment variable. If it is not set when the shell starts, Bash assigns to it the full pathname of the current user’s login shell.

man 5 crontab says:

Several environment variables are set up automatically by the cron(8) daemon. SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd line of the crontab’s owner. PATH is set to “/usr/bin:/bin”. HOME, SHELL, and PATH may be overridden by settings in the crontab

So the SHELL variable is set when the Bash start.

Try SHELL=/bin/awesome/shell bash You should see SHELL=/bin/awesome/shell

Shebang works. You have a documented behaviour:)

Answered By: Evgeny Vereshchagin

Change the shebang to

#!/bin/env bash

Or execute crontab -e to have the script run with your own environment variables, as cron’s own are different.

As a side note, according to this post on askubuntu, you need to give your cron a PATH at the top of the script:


# rest of script follows

However, given that you said PATH seems to have the proper search directories,you can rule that out.

Answered By: ILMostro_7

The shebang is working and cron has nothing to do with that. When a file is executed, if that file’s content begins with #!, the kernel executes the file specified on the #! line and passes it the original file as an argument.

Your problem is that you seem to believe that SHELL in a shell script reflects the shell that is executing the script. This is not the case. In fact, in most contexts, SHELL means the user’s prefered interactive shell, it is meant for applications such as terminal emulator to decide which shell to execute. In cron, SHELL is the variable that tells cron what program to use to run the crontab entries (the part of the lines after the time indications).

Shells do not set the SHELL variable unless it is not set when they start.

The fact that SHELL is /bin/sh is very probably irrelevant. Your script has a #!/bin/bash line, so it’s executed by bash. If you want to convince yourself, add ps $$ in the script to make ps show information about the shell executing the script.

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

    [ "$pid" -gt 0 ] &&
    read -r ppid name < <(ps -o ppid= -o comm= -p "$pid")
    eval $(echo printf '" %0.s"' {0..$depth})
    if [[ -r /proc/$pid/exe ]]; then    
    echo -n "$name - "
    readlink -f /proc/$pid/exe
    echo $name

And a crontab like this:

* * * * * /path/to/ > /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

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.

Answered By: Jaime Hablutzel
Categories: Answers Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.