9

I have a script containing:

#!/bin/bash
printenv

When I run it from the command line:

env testscript.sh
bash testscript.sh
sh testscript.sh

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.

3
  • 2
    Shell scripts in general do not change environment variables so the value of SHELL will not change. First check to see if /bin/sh is a link to bash - it is on many systems. If /bin/sh is something else link dash, try using the bash specific commands you need and see if they fail. Finally, you can use something like '[ -z $BASH ] && { exec bash "$0" "$@" || exit; }' as the first line in your script to make sure you're running it under bash.
    – doneal24
    Jul 15, 2015 at 15:01
  • What distro is this?
    – ILMostro_7
    Jul 15, 2015 at 18:27
  • 3
    Read man 5 crontab. cron sets environment variables SHELL, LOGNAME, HOME and PATH.
    – fd0
    Jul 15, 2015 at 18:45

4 Answers 4

10

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.

2

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 testcript.sh. You should see SHELL=/bin/awesome/shell

Shebang works. You have a documented behaviour:)

0

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.

-1

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:

PATH=/opt/someApp/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# rest of script follows

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

5
  • Benubird is not experiencing behaviour that can be caused by a wrong PATH or a wrong path to the shell.
    – Wieland
    Jul 15, 2015 at 18:36
  • Yah, I saw that bit after I added the part about the PATH; decided to leave it there, nonetheless. This question has multiple versions floating around on here.
    – ILMostro_7
    Jul 15, 2015 at 18:39
  • @Wieland, if his PATH is fine, then env bash should work, no? Ultimately, the question is "How can I make cron apply the shebang"!
    – ILMostro_7
    Jul 15, 2015 at 18:46
  • 1
    Yes, but the path to his bash is already correct, otherwise there would be no output at all.
    – Wieland
    Jul 15, 2015 at 18:47
  • Your downvotes sting like soap in my eyes
    – ILMostro_7
    Jul 15, 2015 at 18:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .