cron not picking up crontab in /etc/cron.d without specific conditions - bash

I'm running Ubuntu 14.04.5 in a container in kubernetes. I'm running cron (successfully) however it's not picking up my crontab unless the minutes field is * or some variation such as */45. Anyone know why this is? The test for this has been to echo to the log and so far it's consistently proving to work that way. /etc/cron.d/crontab file looks like this:
# Run my command
SHELL=/bin/bash
*/47 17 * * * root echo "`date`: About to run!" >> /var/log/cron.log 2>&1
#
If I change this to what I think is standard notation it doesn't work ex:
# Run my command
SHELL=/bin/bash
47 17 * * * root echo "`date`: About to run!" >> /var/log/cron.log 2>&1
#
Is there something I'm missing that is causing this behavior? Note that I have the user specified, I have a trailing new line, and it's in the proper notation. I've also simplified to have just the echo and the same result appears to happen. Any information would be appreciated. I'm baffled by this.

Seems to be a bug in Ubuntu. Assuming that Ubuntu retains the same cron that is in Debian, (it should, but why would canonical do that?) you should be able to use a standard format.
Looking at the code, "*" means a range of "first to last" for whatever the available range is. There are a series of 'if' statements that try to determine the entries, and if certain conditions evaluate a certain way, it will return EOF and bail out on you.
So, */47 in the minute section means 1-59 step (every) 47th minute. This takes one code path, which is AFTER the code path that should say: "at minute 47".
So, if your step path ("/47") is evaluating when the regular one is not evaluating, then it's skipping that code path for some reason.
I've had a lot of weird problems with Ubuntu (like this). One of the reasons we run Debian nearly exclusively on servers.

Related

Cron Job Running Shell Script to Run Python Not Working

As written in the title, I am having some problem with my cron job script not executing. I am using CentOS 7.
My crontab -e looks like this:
30 0 * * * /opt/abc/efg/cron_jobs.sh >> /opt/abc/logs/cron_jobs.log
My cron_jobs.sh looks like this:
#!/bin/bash
#keep this script in efg folder
#run this daily through crontab -e
#45 0 * * * /opt/abc/efg/cron_job.sh
cd "$(dirname "$0")"
export PYTHONPATH=$PYTHONPATH:`pwd`
#some daily jobs script for abc
date
#send email to users whose keys will expire 7 days later
/usr/local/bin/python2.7 scripts/send_expiration_reminder.py -d 7
#send email to key owners whos keys will expire
/usr/local/bin/python2.7 scripts/send_expiration_reminder.py -d -1
# review user follow status daily task
# Need to use venv due to some library dependencies
/opt/abc/virtualenv/bin/python2.7 scripts/review_user_status.py
So, what I've found is that the log for the cron jobs in /var/logs/cron states that the cron ran at 0:30 am accordingly.
Strangely, I find that /opt/abc/logs/cron_jobs.log empty, and the scripts does not seem to run at all. It used to output some log before I re-inputted the crontab (to re-install the cron jobs), and replaced cron_jobs.sh, so I think the problem might have arose from those actions.
And also, I would like to know if there are any ways to log the error from executing a python script. I have been trying to run /opt/abc/virtualenv/bin/python2.7 scripts/review_user_status.py but it never seem to work as intended (does not run the main function at all), and there is no log output whatsoever.
I tried to run this on a different machine and it works properly, so I am not sure what is wrong with the cron job.
Here is a snippet of the log I got from /var/log/cron to show that the cron called the job:
Mar 22 18:32:01 web41 CROND[20252]: (root) CMD (/opt/abc/efg/cron_jobs.sh >> /opt/abc/logs/cron_jobs.log)
There are a few areas to check if you haven't performed these already,
if your executable permissions set on the script,
chmod +x <python file>
in addition permissions for the user to access the directories.
Run the script manually to test the script works from beginning to end, as the user who will be running the script, will be more realistic.
You can test your crontab schedule by temporarily setting every minute for testing, unlike Windows where you can right, click and click Run.
First, thank you all for the suggestions and heads up. I found out that what was ruining my script is the existence of /r in the line break. Apparently, Linux in general does not accept /r and only accepts /n.
It is because I ftp my files to the machine where the script breaks. On the other hand, it works fine on another machine because I used git pull instead of ftp.
Hope that this info will also be a helpful to others!

crontab job does not work

I have a script
5 05 * * * /bin/bash -l -c 'nohup sh test.sh &'>/dev/null 2>&1
It runs prefect in bash but when I put it in crontab it does not work. As it is running on remote server I thought maybe the time zone is the problem. I ran date on the server and the output is Fri Jan 10 05:10:02 UTC 2014. Why it does not work?
You may want to specify the full path to everything. Crons have a hard time finding things if they are not defined explicitly. Also if you have paths in test.sh you may want to specify them as well.
Additionally, if you are having trouble but aren't sure why it's not working if you put MAILTO = 'your email address' at the top and get rid of the output piped to >/dev/null it may help you find out what the error is.
As suggest by #user1332577 Full path of test.sh is needed. Also removind /dev/null part will dump errors.
Also First check cron logs, They may be at /var/log/cron OR /var/log/syslog depends on settings. If their are no entry of this particular cron on specified time. Then obviously cron is not enabled. It also help you check timestamp when it executed.

Cron job does not run

Following is the entry in the crontab:
MAILTO=abc#gmail.com
45 14 * * * /home/user/simple.sh
I've also done chmod +x on the simple.sh But the crontab does not run, it doesn't even send an email.
pgrep cron shows an Id. I also tried bouncing crond. But no luck!
Could someone please point out the mistake here
The simple.sh script is:
#! /bin/bash
echo hello
Thanks
Since you are doing a echo within the cron job script, you need to capture its output somewhere.
Your shebang and file mode (using chmod +x) are all right, so those aren't the issue here and running without /bin/sh should work fine.
Try using the following to see the output in cron.log file (This runs every minute)
* * * * * /home/user/simple.sh >> /home/user/cron.log
Note that cron jobs run in separate subprocess shell, with reduced environment, so its output won't be visible on your terminal.
Regarding sending of email - you need to have some mail package (like postman, mutt etc) configured for the cron daemon to send out error mails.
Do not use relative paths, but absolute ones. Also, indicate the binary running the script, that is /bin/sh (or whatever coming from which sh):
45 14 * * * /bin/sh /path/to/script/simple.sh
Maybe there shouldn't be a space in line 1 of your .sh script:
#! /bin/bash
to
#!/bin/bash
Although I could see why it would still seem to work from when invoked in an interactive shell (# could merely comment out the rest of the line).
Still, I'd guess at worst it'd merely ignore that line and inherit cron's interpreter of /bin/sh

Crontab not executing bash script

I very very rarely use Linux and so don't have any experience with bash scripts and cron jobs.
This is in fact my first attempt. So it's probably something really simple to fix.
I have the following:
/etc/cron.d/clear-mixtape-dir.sh
permissions are: 644
#!/bin/bash
# Clears the /tmp/mixtape2 directory
rm -rf "/tmp/mixtape2/"*
My crontab file looks like so:
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
*/15 * * * * /etc/cron.d/clear-mixtape-dir.sh >/dev/null 2>&1
I'm trying to execute the .sh script every 15 minutes.
Everything i've found says this should work, but it doesn't.
Does anything like file permissions (on files within /tmp/mixtape2/) matter in this case?
Or perhaps the permissions set on the actual .sh script - maybe they need setting to executable?
Any advice appreciated.
Remove the .sh extension from the script in /etc/cron.d and it will be called.
run-parts ignores files with a period in the name, so the .sh extension is preventing your script from running.
From man cron -
Files must conform to the same naming convention as used by run-parts(8): they must consist solely of upper- and lower-case letters, digits, underscores, and hyphens.
Note: These comments refer to /etc/crontab.
Before doing anything else, which cron are you accessing crontab -e or
su -vim
<your-favorite-editor> /etc/crontab
If you are using crontab -e, then no user field exists in that form of crontab. That might be why you're not running.
In your example, your user field is *. I would make it root or a user that has proper permissions.
Before running this program, I would make a dummy crontab entry that just does
echo "Hello" and runs every minute. Get that to work on which ever crontab you're editing (crontab -e or vim /etc/crontab). Then using that as a template, get your script to run.
Next, see if cron is running:
ps -ef | grep cron
If it is not running, become root and start it by enter
/etc/init.d/cron start (Ubuntu and Red Hat).
You already have a good answer suggesting you add root as the user because of a permissions problem. I'm going to suggest more things to help you debug. I have run into a lot of cron problems over the years.
1) Set the email to a known address, unless you will continually monitor root's email
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/local/bin:/usr/bin
MAILTO=fred#somewhere.com
HOME=/
2) Until everything runs properly, take out the >/dev/null 2>&1 out of your cron entry, so you see the outputs in your email generated after the script runs.
3) Bump */15 down to an interval greater than it takes your script to run -- likr */5, so the script runs more often.
4) I do not know the exact reason, but scripts I run out of cron have to set up their own environments despite being run as that user in cron. This may include steps like cd /home/script-owner and running source .bashrc and calling other script(s) that set environment variables.
*/15 * * * * root /etc/cron.d/clear-mixtape-dir.sh >/dev/null 2>&1
Add user root because your permission seems to be only for root.

Can a bash script tell if it's being run via cron?

Not having much luck Googling this question and I thought about posting it on SF, but it actually seems like a development question. If not, please feel free to migrate.
So, I have a script that runs via cron every morning at about 3 am. I also run the same scripts manually sometimes. The problem is that every time I run my script manually and it fails, it sends me an e-mail; even though I can look at the output and view the error in the console.
Is there a way for the bash script to tell that it's being run through cron (perhaps by using whoami) and only send the e-mail if so? I'd love to stop receiving emails when I'm doing my testing...
you can try "tty" to see if it's run by a terminal or not. that won't tell you that it's specifically run by cron, but you can tell if its "not a user as a prompt".
you can also get your parent-pid and follow it up the tree to look for cron, though that's a little heavy-handed.
I had a similar issue. I solved it with checking if stdout was a TTY. This is a check to see if you script runs in interactive mode:
if [ -t 1 ] ; then
echo "interacive mode";
else
#send mail
fi
I got this from: How to detect if my shell script is running through a pipe?
The -t test return true if file descriptor is open and refers to a terminal. '1' is stdout.
Here's two different options for you:
Take the emailing out of your script/program and let cron handle it. If you set the MAILTO variable in your crontab, cron will send anything printed out to that email address. eg:
MAILTO=youremail#example.com
# run five minutes after midnight, every day
5 0 * * * $HOME/bin/daily.job
Set an environment variable in your crontab that is used to determine if running under cron. eg:
THIS_IS_CRON=1
# run five minutes after midnight, every day
5 0 * * * $HOME/bin/daily.job
and in your script something like
if [ -n "$THIS_IS_CRON" ]; then echo "I'm running in cron"; else echo "I'm not running in cron"; fi
Why not have a command line argument that is -t for testing or -c for cron.
Or better yet:
-e=email#address.com
If it's not specified, don't send an email.
I know the question is old, but I just came across the same problem. This was my solution:
CRON=$(pstree -s $$ | grep -q cron && echo true || echo false)
then test with
if $CRON
then
echo "Being run by cron"
else
echo "Not being run by cron"
fi
same idea as the one that #eruciform mentioned - follows your PID up the process tree checking for cron.
Note: This solution only works specifically for cron, unlike some of the other solutions, which work anytime the script is being run non-interactively.
What works for me is to check $TERM. Under cron it's "dumb" but under a shell it's something else. Use the set command in your terminal, then in a cron-script and check it out
if [ "dumb" == "$TERM" ]
then
echo "cron"
else
echo "term"
fi
I'd like to suggest a new answer to this highly-voted question. This works only on systemd systems with loginctl (e.g. Ubuntu 14.10+, RHEL/CentOS 7+) but is able to give a much more authoritative answer than previously presented solutions.
service=$(loginctl --property=Service show-session $(</proc/self/sessionid))
if [[ ${service#*=} == 'crond' ]]; then
echo "running in cron"
fi
To summarize: when used with systemd, crond (like sshd and others) creates a new session when it starts a job for a user. This session has an ID that is unique for the entire uptime of the machine. Each session has some properties, one of which is the name of the service that started it. loginctl can tell us the value of this property, which will be "crond" if and only if the session was actually started by crond.
Advantages over using environment variables:
No need to modify cron entries to add special invocations or environment variables
No possibility of an intermediate process modifying environment variables to create a false positive or false negative
Advantages over testing for tty:
No false positives in pipelines, startup scripts, etc
Advantages over checking the process tree:
No false positives from processes that also have crond in their name
No false negatives if the script is disowned
Many of the commands used in prior posts are not available on every system (pstree, loginctl, tty). This was the only thing that worked for me on a ten years old BusyBox/OpenWrt router that I'm currently using as a blacklist DNS server. It runs a script with an auto-update feature. Running from crontab, it sends an email out.
[ -z "$TERM" ] || [ "$TERM" = "dumb" ] && echo 'Crontab' || echo 'Interactive'
In an interactive shell the $TERM-variable returns the value vt102 for me. I included the check for "dumb" since #edoceo mentioned it worked for him. I didn't use '==' since it's not completely portable.
I also liked the idea from Tal, but also see the risk of having undefined returns. I ended up with a slightly modified version, which seems to work very smooth in my opinion:
CRON="$( pstree -s $$ | grep -c cron )"
So you can check for $CRON being 1 or 0 at any time.

Resources