[SOLVED] bash script runs perfectly when started manually - but not when started by CRON
Linux - GeneralThis Linux forum is for general Linux questions and discussion.
If it is Linux Related and doesn't seem to fit in any other forum then this is the place.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
bash script runs perfectly when started manually - but not when started by CRON
Hi guys,
First post here - but long time reader.
I have a small bash problem, which seems to be something I have done wrong in the scripts - but I can't identify the problem.
I am using Raspbian on my Raspberry Pi, and I wish to create a mirror of two harddrives using the RSYNC function via a cron job. And so far it has been working, but after I made some amendments to the bash, it will no longer run correctly as a cron job.
The cron job looks like this:
Code:
05 07 * * * cd /home/pi/.scripts && sh rsync.sh
And the bash looks like this:
Code:
#!/bin/bash
#Get timestamp
time="$(date +"%k%M")"
#Geting date
datestamp="$(date +"%Y%m%d")"
year="$(date +"%Y")"
#Script runs from this time
time_begin="658"
#Script runs before this time
time_end="1200"
# The Sync
echo 'Syncronizing files' >> /media/slave/.rsync-log/rsync-$datestamp.log
if [[ "$time" -ge "$time_begin" ]] && [[ "$time" -le "$time_end" ]];then
rsync -av --log-file=/media/slave/.rsync-log/rsync-$datestamp.log --delete --exclude=".*" /media/slave/files/ /media/master/files/
sleep 5
else
echo 'It is not time to sync the files' >> /media/slave/.rsync-log/rsync-$datestamp.log
fi
As you can see if the time is between 6:58 and 12:00 hrs, then the sync should run - and if not, it returns "It is not time to sync the files" and but that in the log file.
When I run the script manually between the said times it works like a charm. The sync starts and the info is put in the log file as requested.
However, when CRON runs the script it returns the "It is not time to sync the files" information, no matter what time of the day it's run including the previously mentioned time intervals. The script still puts the info successfully into the log file though, which tells me that part works OK.
Can you guys identify the problem as I seem to be at a dead end right now.
Thanks in advances.
Jonas
Last edited by jonasbdk; 08-23-2014 at 02:22 AM.
Reason: minor spelling/grammar mistakes
There is not enough here to be sure we can identify the problem, but....
Why not call the script from crontab using the fulel path name and avoid stacking two commands?
ie, instead of 'cd path && sh script', make the script executable and just use the command 'path/script'.
Check the PATH used by cron as opposed to the PATH in your session. It is very likely that they differ, and that can cause problems. To resolve, se the PATH environment variable correctly early in the script.
Once you have experimented with these factors, return to describe your results.
There is not enough here to be sure we can identify the problem, but....
Why not call the script from crontab using the fulel path name and avoid stacking two commands?
ie, instead of 'cd path && sh script', make the script executable and just use the command 'path/script'.
Check the PATH used by cron as opposed to the PATH in your session. It is very likely that they differ, and that can cause problems. To resolve, se the PATH environment variable correctly early in the script.
Once you have experimented with these factors, return to describe your results.
Thanks for your swift reply. But how do I check which PATH is used by cron?
and the script is running under a different shell. Then the "if" command may fail due to a syntax error, perhaps because double brackets [[ ]] are not interpreted in the way you expected.
Note added in a later edit: A syntax error within an if command doesn't necessarily give an error message because the if command will still do something. It just won't do what you expected. Thus it might be correct to say the if command fails due to a logic error, but the logic error is due to syntax.
There may be several ways to correct this.
The method that wpeckham suggested will probably work because (I think) it uses bash by default (Edit: actually the reason is because this method of invocation uses the #! directive):
Quote:
Originally Posted by wpeckham
Why not call the script from crontab using the full path name and avoid stacking two commands?
ie, instead of 'cd path && sh script', make the script executable and just use the command 'path/script'.
That's what I would have done. In other words, your cron line would look like this:
Code:
05 07 * * * /home/pi/.scripts/rsync.sh
And that should work as long as the script file rsync.sh has its permission set to executable for the user in whose account cron is running.
Another possible solution is to run the script (in cron) with bash instead of sh.
I agree with pwalden that you could eliminate the time test altogether, since cron would only ever run the script at the specified time.
Actually, the cron simply uses the default system path, often very minimal.
To be safe, check the location of every executable you will call using the script (date for example, is an external, as is rsync) and make sure you set a path in the script to includes all of those locations.
I also agree with the above comment about the time checks. If you are using crontab to CONTROL when the script fires off, you do not really need a second check within the script.
There are other considerations here, involving effiency and CPU cycles, but they are trivial. The more important thing is what goes on in your mind as you read the script. Keep it simple, clear, and elegant for ease of maintenance over the long term.
The most robust solution is for the script to set the PATH it needs; that works regardless of how cron is configured.
For example (including some other defensive settings):
Code:
# Configure shell environment
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~
export PATH=/usr/sbin:/sbin:/usr/bin:/bin
IFS=$' \n\t'
unset CDPATH # Ensure cd behaves as expected
umask 022
Unfortunately this input failed. I could not install a new crontab.
Code:
crontab: installing new crontab
"/tmp/crontab.ZCmdYs/crontab":7: bad command
errors in crontab file, can't install.
Do you want to retry the same edit? (y/n)
and the script is running under a different shell. Then the "if" command may fail due to a syntax error, perhaps because double brackets [[ ]] are not interpreted in the way you expected.
Note added in a later edit: A syntax error within an if command doesn't necessarily give an error message because the if command will still do something. It just won't do what you expected. Thus it might be correct to say the if command fails due to a logic error, but the logic error is due to syntax.
There may be several ways to correct this.
The method that wpeckham suggested will probably work because (I think) it uses bash by default (Edit: actually the reason is because this method of invocation uses the #! directive):
That's what I would have done. In other words, your cron line would look like this:
Code:
05 07 * * * /home/pi/.scripts/rsync.sh
And that should work as long as the script file rsync.sh has its permission set to executable for the user in whose account cron is running.
Another possible solution is to run the script (in cron) with bash instead of sh.
I agree with pwalden that you could eliminate the time test altogether, since cron would only ever run the script at the specified time.
This worked. I just removed the sh in front of the path to the script, as it was executable already.
This worked. I just removed the sh in front of the path to the script, as it was executable already.
Man, it's hard to be a newbie to this bash thing
Anyway - thank you very much for your help.
Glad to hear it!
If it's no trouble, go back to my previous post and click where it says Did you find this post helpful? Thanks.
(Pardon me, since you're a new member, that option may not be available. Nevermind!
but you can still mark the thread as solved.)
It looks like you have solved your original problem, but would you like to understand what went wrong here?
Quote:
Originally Posted by jonasbdk
The output from env was:
...
I tried this.
...
Unfortunately this input failed. I could not install a new crontab.
...
Code:
crontab: installing new crontab
"/tmp/crontab.ZCmdYs/crontab":7: bad command
errors in crontab file, can't install.
Do you want to retry the same edit? (y/n)
I think you did something different from what they suggested.
First you put the env command in the script, and redirect its output to an output file:
Code:
#!/bin/bash
env >> temp.txt
Check that the script is executable, or make it so.
Edit crontab so that it will execute the script:
Code:
00 04 * * * /home/beryllos/temp.sh
Wait until the appointed time and then inspect the output:
That's funny! It shows SHELL=/bin/sh, not /bin/bash. Is that significant? On my system, apparently not. My scripts running under crontab seem to be interpreted according to bash syntax, regardless of this SHELL=/bin/sh environment variable. I am not sure why. Perhaps, I guess, it is in fact following the #!/bin/bash directive, without regard to the SHELL variable.
Edit: Confirm that. There is another method to determine which command interpreter is being used for a script:
Unfortunately this input failed. I could not install a new crontab.
Code:
crontab: installing new crontab
"/tmp/crontab.ZCmdYs/crontab":7: bad command
errors in crontab file, can't install.
Do you want to retry the same edit? (y/n)
The lines were intended for the script, not for the crontab.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.