Bash script runs in shell, gives "not found" error in crontab - bash

I am using an EC2 instance, crontab, and slack-cleaner to delete all Slack messages older than 48 hours. To do this, I created delete_slack.sh (I've deleted my slack api token):
for CHANNEL in random general
do
slack-cleaner --token <MY TOKEN> --message --channel $CHANNEL --user "*" --before $(date -d '48 hour ago' "+%Y%m%d") --perform
done
Then I created a crontab line to run it every minute (once it works I'll change the timing to once a day) and had cron spit out the results to a log file:
* * * * * /home/ubuntu/delete_slack/delete_slack.sh >> /var/log/delete_slack.log 2>&1
To test, I ran sh /home/ubuntu/delete_slack/delete_slack.sh >> /var/log/delete_slack.log 2>&1 in the shell and it works fine. However, when I let the crontab run I get an error in the log file:
/home/ubuntu/delete_slack/delete_slack.sh: 3: /home/ubuntu/delete_slack/delete_slack.sh: slack-cleaner: not found
Any ideas? I've been banging my head against this all afternoon.

Sounds like the PATH you get via cron and the PATH you get through your login are different.
Either set the PATH in your script or use the absolute path to slack-cleaner
The PATH tells the shell which directories to search for executables (including scripts). You can echo $PATH to compare your path to the one cron gives and confirm that this is the issue.
If using the absolute path works, that is simplest, but if slack-cleaner uses other exes itself, setting the path may be better.
If you want to go the "modify PATH" method then you want to append the correct path to existing PATH and not completely overwrite it. i.e. export PATH=$PATH:/path/to/slack-cleaner-dir. You can always use which slack-cleaner to find out the correct path. NOTE: you want the directory without "slack-cleaner" appended to the end.

ALWAYS use full path in crons and you'll save a lot of time.
If you don't like export PATH=... then just use /path/to/slack-cleaner-dir instead.

Just load your profile before running the command to be in the exact same situation as when you launch it from your shell :
* * * * * . ~/.profile;/home/ubuntu/delete_slack/delete_slack.sh >> /var/log/delete_slack.log 2>&1
As I read that you're a bit new to this, here are just some more explanations about the profile :
The profile is a file loaded automatically when you connect in shell with your user.
The file is hidden in your home directory, to see it, you can launch :
ls -la ~
If you're in bash, the file will be named .bash_profile, if you're in shell or ksh, it will be named .profile
Hope it helped !

Related

How to format the cron url when driving to a shell script

I have a shell program name myshellscript.sh and not having any luck with getting it to run by cron. Can anyone see what i am missing.. Runs perfect when using the shell terminal.. but just don't have the URL right yet to fire it off.
php /home/myuser/public_html/usr/local/cpanel/scripts/myshellscript.sh
Here is my latest attempt that does not work.. I have used WGET vice PHP before with a full URL but just have no idea if I am onthe right track or not..
Without a specific error message beyond you saying that it does not work, we can only speculate on 'common issues' that prevent cron jobs from running, i.e.: user and file permissions, missing path/environmental info, etc.
Check man page for cron - note information about setting path and environment.
Some operating systems support setting environment option directly in your 'crontab file'; others may require using full paths to executables, or perhaps allow you to 'refine' the PATH variable in your script...
Try capturing/logging cron 'errors', i.e.
*/5 * * * * /some_path/your_script.sh 2>&1 >> /tmp/cron.script.log
The above line well send both STD-out and STD-error to the file shown, and, it will 'run' every 5 minutes.
:)
Dale

Keeping a terminal window open after running script from crontab

I have this script
#!/bin/sh
curl -4 http://wttr.in/Colorado\ Springs
that I want to automatically execute each morning. I have my crontab entry as
* 7 * * * (path to script)
But either the script doesnt run, or it runs and then immediately closes the shell. I know that my cronjobs are running as I have other scripts for backups that run on an hourly basis but cant figure out what detail I am missing here. I found one suggestion to include $SHELL in the script but that made no difference. Any suggestions?
Usually when I have to keep the terminal open I would exec bash as my last command. I do that when I write installer script which would open terminal; do the job and get lost after that. But if there is an error then I want the terminal to stay there so that I can read the error.
exec is used to replace the current program with argument which we provide to exec.
Actually, I don't know what are you trying to achieve with this call in your crontab. Do you want to see the weather report on your terminal? Do you want to save the weather report in the file? Get it in your emails?
If you do no redirections, you'll get the report in your mail.
If you want to have it in a file, just do:
curl wttr.in/Colorado+Springs > file
If you want to have it on you terminals do
curl wttr.in/Colorado+Springs | wall
Please note that you don't need -4, http:// and you can replace \space with +.
(DISCLAIMER: I'm the author of wttr.in)

Crontab won't run sed shell

I have a cron job to run a sed shell called sedcmd.sh to pre-process some json data. When i am in the proper directory I manually run it with
. ./sedcmd.sh
And it works. The shell itself works fine.
for reference one of the commands inside looks like
sed -i '/^\s*$/d' /home/school/Desktop/Programs/rawjsondata.txt
my cronjob looks like
5 * * * * . ./home/school/Desktop/Programs/sedcmd.sh
I get the error "No such file or directory found". What am I doing wrong. I've triple checked for any random spelling errors. I also can't seem to run sedcmd.sh from any other directory even when i give the entire file path, so its definitely something I'm doing wrong. My thoughts for solutions are either
I should add sedcmd.sh to my $PATH or bashrc so i can call it from anywhere. Which I don't know how to do.
OR
figure out how to call it correctly from crontab. Which I also dont know how to do.
When you are running a script from terminal, you do:
./script_name.sh but to execute the same script from crontab you do something like 5 * * * * /path/to/script/script_name.sh
As Sam has got this answer from the comments,posting the answer as community wiki.

Strange hg log output

Each morning at 10:00 crontab is setup to run this command in a bash script (the below dates are an example, they are calculated using the linux date command in the script):
hg log -R some-repo -b some-branch --date "2012-11-28 10:00 to 2012-11-29 10:00"
The above command is run on the server with the remote repo where all developers push their code. The output is stored in a string and send to me via email. But its always empty! If I run the script manually a few minutes later I get the expected output.
I have compared the history before the script is run by crontab and verified that something has indeed been committed/pushed to the remote repo.
Any ideas why running the above script AFTER 10:00 gives the correct output and not when run at 10:00?
In the script I do this:
logString=$(hg log -R "$path-to-repo" -b $branch --date "$YESTERDAY to $TODAY")
if [ -z "$logString" ]; then
logString="Nothing"
fi
EDIT: The hg log is ONLY empty when the script is run by crontab.
SOLVED: I needed to specify the full path to hg (/usr/local/bin/hg) for cron to see it. A bit strange since it does not need have the full path to svn and it has worked fine previously without the full path to hg.
How do I see what cron has in its PATH?
crontab has a very limited environment. If you are assuming that certain variables are going to be available from your .profile (or equivalent) it's most likely not going to be there. If you need it you will have to source it into your script.
It also looks like you you have included a snippet of a longer script. Make the simplest script that should work and add it to the cron. That will make it easier to debug. For example, if you hard-code the dates in the script, you should be able to get it to work. Once you have that working you can gradually add complexity until you figure out what's not working.

How to test things in crontab

This keeps happening to me all the time:
1) I write a script(ruby, shell, etc).
2) run it, it works.
3) put it in crontab so it runs in a few minutes so I know it runs from there.
4) It doesnt, no error trace, back to step 2 or 3 a 1000 times.
When I ruby script fails in crontab, I can't really know why it fails cause when I pipe output like this:
ruby script.rb >& /path/to/output
I sorta get the output of the script, but I don't get any of the errors from it and I don't get the errors coming from bash (like if ruby is not found or file isn't there)
I have no idea what environmental variables are set and whether or not it's a problem. Turns out that to run a ruby script from crontab you have to export a ton of environment variables.
Is there a way for me to just have crontab run a script as if I ran it myself from my terminal?
When debugging, I have to reset the timer and go back to waiting. Very time consuming.
How to test things in crontab better or avoid these problems?
"Is there a way for me to just have crontab run a script as if I ran it myself from my terminal?"
Yes:
bash -li -c /path/to/script
From the man page:
[vindaloo:pgl]:~/p/test $ man bash | grep -A2 -m1 -- -i
-i If the -i option is present, the shell is interactive.
-l Make bash act as if it had been invoked as a login shell (see
INVOCATION below).
G'day,
One of the basic problems with cron is that you get a minimal environment being set by cron. In fact, you only get four env. var's set and they are:
SHELL - set to /bin/sh
LOGNAME - set to your userid as found in /etc/passwd
HOME - set to your home dir. as found in /etc/passwd
PATH - set to "/usr/bin:/bin"
That's it.
However, what you can do is take a snapshot of the environment you want and save that to a file.
Now make your cronjob source a trivial shell script that sources this env. file and then executes your Ruby script.
BTW Having a wrapper source a common env. file is an excellent way to enforce a consistent environment for multiple cronjobs. This also enforces the DRY principle because it gives you just one point to update things as required, instead of having to search through a bunch of scripts and search for a specific string if, say, a logging location is changed or a different utility is now being used, e.g. gnutar instead of vanilla tar.
Actually, this technique is used very successfully with The Build Monkey which is used to implement Continuous Integration for a major software project that is common to several major world airlines. 3,500kSLOC being checked out and built several times a day and over 8,000 regression tests run once a day.
HTH
'Avahappy,
Run a 'set' command from inside of the ruby script, fire it from crontab, and you'll see exactly what's set and what's not.
To find out the environment in which cron runs jobs, add this cron job:
{ echo "\nenv\n" && env|sort ; echo "\nset\n" && set; } | /usr/bin/mailx -s 'my env' you#example.com
Or send the output to a file instead of email.
You could write a wrapper script, called for example rbcron, which looks something like:
#!/bin/bash
RUBY=ruby
export VAR1=foo
export VAR2=bar
export VAR3=baz
$RUBY "$*" 2>&1
This will redirect standard error from ruby to the standard output. Then you run rbcron in your cron job, and the standard output contains out+err of ruby, but also the "bash" errors existing from rbcron itself. In your cron entry, redirect 2>&1 > /path/to/output to get output+error messages to go to /path/to/output.
If you really want to run it as yourself, you may want to invoke ruby from a shell script that sources your .profile/.bashrc etc. That way it'll pull in your environment.
However, the downside is that it's not isolated from your environment, and if you change that, you may find your cron jobs suddenly stop working.

Resources