Check if script is started by cron, rather than invoked manually
Solution 1
I'm not aware that cron
does anything to its environment by default that can be of use here, but there are a couple of things you could do to get the desired effect.
1) Make a hard or soft link to the script file, so that, for example, myscript
and myscript_via_cron
point to the same file. You can then test the value of $0
inside the script when you want to conditionally run or omit certain parts of the code. Put the appropriate name in your crontab, and you're set.
2) Add an option to the script, and set that option in the crontab invocation. For example, add an option -c
, which tells the script to run or omit the appropriate parts of the code, and add -c
to the command name in your crontab.
And of course, cron can set arbitrary environment variables, so you could just put a line like RUN_BY_CRON="TRUE"
in your crontab, and check its value in your script.
Solution 2
Scripts run from cron are not run in interactive shells. Neither are startup scripts. The differentiation is that interactive shells have STDIN and STDOUT attached to a tty.
Method 1: check if $-
includes the i
flag. i
is set for interactive shells.
case "$-" in
*i*)
interactive=1
;;
*)
not_interactive=1
;;
esac
Method 2: check is $PS1
is empty.
if [ -z "$PS1" ]; then
not_interactive=1
else
interactive=1
fi
references:
- https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html
- https://www.gnu.org/software/bash/manual/html_node/Is-this-Shell-Interactive_003f.html
Method 3: test your tty. it's not as reliable, but for simple cron jobs you should be ok, as cron does not by default allocate a tty to a script.
if [ -t 0 ]; then
interactive=1
else
non_interactive=1
fi
Keep in mind that you can however force an interactive shell using -i
, but you'd probably be aware if you were doing this...
Solution 3
First, get cron's PID, then get the current process's parent PID (PPID), and compare them:
CRONPID=$(ps ho %p -C cron)
PPID=$(ps ho %P -p $$)
if [ $CRONPID -eq $PPID ] ; then echo Cron is our parent. ; fi
If your script is started by another process that might have been started by cron, then you can walk your way back up the parent PIDs until you get to either $CRONPID or 1 (init's PID).
something like this, maybe (Untested-But-It-Might-Work<TM>):
PPID=$$ # start from current PID
CRON_IS_PARENT=0
CRONPID=$(ps ho %p -C cron)
while [ $CRON_IS_PARENT -ne 1 ] && [ $PPID -ne 1 ] ; do
PPID=$(ps ho %P -p $PPID)
[ $CRONPID -eq $PPID ] && CRON_IS_PARENT=1
done
From Deian: This is a version tested on RedHat Linux
# start from current PID
MYPID=$$
CRON_IS_PARENT=0
# this might return a list of multiple PIDs
CRONPIDS=$(ps ho %p -C crond)
CPID=$MYPID
while [ $CRON_IS_PARENT -ne 1 ] && [ $CPID -ne 1 ] ; do
CPID_STR=$(ps ho %P -p $CPID)
# the ParentPID came up as a string with leading spaces
# this will convert it to int
CPID=$(($CPID_STR))
# now loop the CRON PIDs and compare them with the CPID
for CRONPID in $CRONPIDS ; do
[ $CRONPID -eq $CPID ] && CRON_IS_PARENT=1
# we could leave earlier but it's okay like that too
done
done
# now do whatever you want with the information
if [ "$CRON_IS_PARENT" == "1" ]; then
CRON_CALL="Y"
else
CRON_CALL="N"
fi
echo "CRON Call: ${CRON_CALL}"
Solution 4
If your script file is invoked by cron
and it contains a shell in the first line like #!/bin/bash
you need to find the parent-parent name for your purpose.
1) cron
is invoked at the given time in your crontab
, executing a shell
2) shell executes your script
3) your script is running
The parent PID is available in bash as variable $PPID
. The ps
command to get the parent PID of the parent PID is:
PPPID=`ps h -o ppid= $PPID`
but we need the name of the command, not the pid, so we call
P_COMMAND=`ps h -o %c $PPPID`
now we just need to test the result for "cron"
if [ "$P_COMMAND" == "cron" ]; then
RUNNING_FROM_CRON=1
fi
Now you can test anywhere in your script
if [ "$RUNNING_FROM_CRON" == "1" ]; then
## do something when running from cron
else
## do something when running from shell
fi
Good luck!
Solution 5
This bash
function should work on systems with either cron
or crond
.
Tested under Debian bullseye.
## Returns 0 (success) if we are running under Cron
function undercron ()
{
local cronpid=$(pgrep --uid=root --oldest --exact '^crond?$')
for ((ppid=PPID; ppid > 1; ppid=$(ps ho %P -p $ppid))); do
if ((ppid == cronpid)); then
return 0
fi
done
return 1
}
Related videos on Youtube
daisy
Updated on September 18, 2022Comments
-
daisy almost 2 years
Is there any variable that cron sets when it runs a program ? If the script is run by cron, I would like to skip some parts; otherwise invoke those parts.
How can I know if the Bash script is started by cron ?
-
Alessio almost 12 years+1 for RUN_BY_CRON=true
-
mveroone over 8 yearsNote that the $PS1 command does not work when checking if script is started by systemd or not. the $- one does
-
Deian almost 8 yearsthe answer by cas is working very well and can be used for anything else too
-
ceving over 7 yearsOn Solaris cron starts a shell and the shell runs the script, which itself starts another shell. So the parent pid in the script is not the pid of cron.
-
WinEunuuchs2Unix over 7 yearsYour Winnipeg University link is broken.
-
WinEunuuchs2Unix over 7 years@TimKennedy You're welcome.... from Edmonton :)
-
Hobadee about 7 years'case "$-" in' doesn't appear to work in bash scripts.
-
Tim Kennedy about 7 years@Hobadee - every
bash
i have access to has $-, as dodash
andksh
. even the restricted shells in Solaris have it. What platform are you trying to use it where it's not working? What doescase "$-" in *i*) echo true ;; *) echo false ;; esac
show you? -
Justin over 4 yearsThis works only for Linux ps. For MacOS (as well as Linux, maybe *BSD too), you can use the following P_COMMAND:
P_COMMAND=$(basename -a $(ps h -o comm $PPPID))
-
saraedum about 4 years
case "$-" in *i*) echo true ;; *) echo false ;; esac
saystrue
when I run it directly on the prompt. However, when putting it in ascript.sh
and running it withbash script.sh
it saysfalse
. So this does not work for me to detect whether the script is being run from cron or invoked directly on an interactive shell. -
Tim Kennedy about 4 years@saraedum when you run
bash script.sh
, the shell that's invoked isn't allocated a tty, and isn't actually an interactive shell. So this is actually working as intended. See the difference betweenecho $-
andbash -c 'echo $-'
. -
Tim Kennedy about 4 years@saraedum see this page for more clarity on interactive and non-interactive shells: tldp.org/LDP/abs/html/intandnonint.html
-
they over 2 yearsI can't see those environment variables on any Unix I have interactive access to. But what you are basically saying is the inverse of the suggestion at the end of the accepted answer: Test for the absense of some variable that is usually declared in an interactive shell.
-
mgutt over 2 years@they Thank you for testing. Does SSH_CONNECTION exist in Unix if you connect to the Terminal through SSH? That's an additional variable which could be used in Linux, but it does not exist if the Terminal on the machine itself is used of course.
-
they over 2 yearsYou could test for
PS1
, the interactive prompt. This is essentially what another answer already suggests, though. -
mgutt over 2 yearsYes, but this does not cover situations when you execute a script by passing the SSH command as a parameter as mentioned in this answer.
-
they over 2 yearsYes, as the answer that you link to mentions, it is safer to give the script an indication that it is running from cron (which is the specific situation that we want to test for) than relying on what the environment usually looks like. Personally, I would run the script with
FROM_CRON=true ./myscript
in the cron schedule, and then test forFROM_CRON
in the script (if [ "${FROM_CRON-false}" = true ]; then ...; fi
), rather than abusing the command line options. It's unclear why this has not been suggested so far. (EDIT: Oh, yes, it's in the accepted answer).