Bash – Can you execute a Bash function with the `at` command

atbashenvironment-variablesfunction

I want to execute a Bash function at a scheduled time. I think the right tool for this is the at command.

However, it doesn't seem to be working.

function stupid () {
    date
}

export -f stupid

cat << EOM | at now + 1 minute
stupid
EOM

After waiting the minute, I check my mail and this is the error I see:

sh: line 41: syntax error near unexpected token `=\(\)\ {\ \ date"
"}'
sh: line 41: `"}; export BASH_FUNC_stupid()'

I don't understand what's going wrong here, especially since I know the function works.

$ stupid
Fri May 29 21:05:38 UTC 2015

Looking at the error, I think the incorrect shell is being used to execute the function (sh as opposed to bash), but if I check $SHELL I see it points to /bin/bash, and the man page for at says:

$ echo $SHELL
/bin/bash
$ man at

    ...

    SHELL   The value of the SHELL environment variable at the time
           of at invocation will determine which shell is used  to
           execute  the at job commands.

So Bash should be the shell running my function.

What going on here? Is there a way to get my Bash function to run with at?

Best Answer

Bash functions are exported via the environment. The at command makes the environment, the umask and the current directory of the calling process available to the script by generating shell code that reproduces the environment. The script executed by your at job is something like this:

#!/bin/bash
umask 022
cd /home/nick
PATH=/usr/local/bin:/usr/bin:/bin; export PATH
HOME=/home/nick; export HOME
…
stupid

Under older versions of bash, functions were exported as a variable with the name of the function and a value starting with () and consisting of code to define the function, e.g.

stupid="() {
    date
}"; export stupid

This made many scenarios vulnerable to a security hole, the Shellshock bug (found by Stéphane Chazelas), which allowed anyone able to inject the content of an environment variable under any name to execute arbitrary code in a bash script. Versions of bash where with a Shellshock fix use a different way: they store the function definition in a variable whose name contains characters that are not found in environment variables and that shells do not parse as assignments.

BASH_FUNC_stupid%%="() {
    date
}"; export stupid

Due to the %, this is not valid sh syntax, not even in bash, so the at job fails, whether it even attempts to use the function or not. The Debian version of at, which is used in many Linux distributions, was changed in version 3.16 to export only variables that have valid names in shell scripts. Thus newer versions of at don't pass post-Shellshock bash exported functions through, whereas older ones error out.

Even with pre-Shellshock versions of bash, the exported function only works in bash scripts launched from the at job, not in the at job itself. In the at job itself, even if it's executed by bash, stupid is just another environment variable; bash only imports functions when it starts.

To export functions to an at job regardless of the bash or at version, put them in a file and source that file from your job, or include them directly in your job. To print out all defined functions in a format that can be read back, use declare -f.

{ declare -f; cat << EOM; } | at now + 1 minute
stupid
EOM
Related Question