Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a shell script to install cron job

This is the first time i am writing a shell script and i have very little information in the given timeline. Though i am reading through different tutorials but i thought to ask what i want here as well.

I want to write a shell script, which on any machine, edit the cronjob, add a new script to be executed at every 15 minutes. so basically i have to add an entry

0,15,30,45 * * * * /home/personal/scripts/cronSqlprocedure.sh

What i want in the shell script

  • it would first change the permissions/execution rights for cronSqlprocedure.sh
  • edit existing cron job and add this new entry into it.

If possible, I would like to write cronSqlprocedure through the shell script too, since it requires couple of variables which may varry from system to system.

export ORACLE_HOME=/opt/app/oracle/product/11.2.0/dbhome_1
export PATH=$ORACLE_HOME/bin:$PATH
export ORACLE_SID=HEER

These lines have to be configured for each machine in the cronSqlprocedure.sh.

#!/bin/bash
ORACLE_HOME="/opt/app/oracle/product/11.2.0/dbhome_1"
ORACLE_SID="HEER"
ORACLE_USER="USER1"
ORACLE_PASSWORD="USERPASS"

echo "export ORACLE_HOME=$ORACLE_HOME" >> $PWD/sqlcronprocedure.sh
echo "export PATH=\$ORACLE_HOME/bin:\$PATH" >> $PWD/sqlcronprocedure.sh
echo "export ORACLE_SID=$ORACLE_SID" >> $PWD/sqlcronprocedure.sh
echo "rTmpDir=/tmp" >> $PWD/sqlcronprocedure.sh

echo "sqlplus -s $ORACLE_USER@$ORACLE_SID/$ORACLE_PASSWORD  > $rTmpDir/deleteme.txt 2>&1 <<EOF" >> $PWD/sqlcronprocedure.sh
echo "    select 1 from dual;" >> $PWD/sqlcronprocedure.sh
echo "    execute another_script(1000,14);" >> $PWD/sqlcronprocedure.sh
echo "EOF" >> $PWD/sqlcronprocedure.sh

chmod 755 $PWD/sqlcronprocedure.sh

crontab -l > $PWD/sqlcorn.sh
echo "0,15,30,45 * * * * $PWD/sqlcronprocedure.sh" >> $PWD/sqlcorn.sh
crontab $PWD/sqlcorn.sh
like image 764
Em Ae Avatar asked Jan 31 '12 16:01

Em Ae


1 Answers

Simple Answer to Original Question

It all seems like routine shell scripting:

# Clobber previous edition of script!
cronscript=$HOME/scripts/cronSqlprocedure.sh
cat <<EOF > $cronscript
export ORACLE_HOME=/opt/app/oracle/product/11.2.0/dbhome_1
export PATH=\$ORACLE_HOME/bin:\$PATH
export ORACLE_SID=HEER
...and whatever else is needed...
EOF
chmod u+x $cronscript

# Add to crontab
tmp=${TMPDIR:-/tmp}/xyz.$$
trap "rm -f $tmp; exit 1" 0 1 2 3 13 15
crontab -l | sed '/cronSqlprocedure.sh/d' > $tmp  # Capture crontab; delete old entry
echo "0,15,30,45 * * * * $cronscript" >> $tmp
crontab < $tmp
rm -f $tmp
trap 0

The trap stuff ensures minimum damage if the user decides to interrupt, cleaning up the temporary file. Note that the old version of the script, if any, has already been clobbered. If you wanted to, you could arrange to create the script into another temp file, and only finish the moving when your satisfied. I typically use I/O redirection on the crontab command; you can perfectly well supply the file name as an argument.

Note the escapes on \$ORACLE_HOME and \$PATH that William Pursell correctly pointed out should be present on \$ORACLE_HOME and should (perhaps) be present on \$PATH. You need to decide whether you want to take the cron-provided (totally minimal) value of $PATH (in which case you want the backslash) or whether you want to use the user's current value of $PATH in the cron script. Either could be correct - just be aware of which you choose and why. Remember, the environment provided by cron is always minimal; you will get a setting for PATH, HOME, maybe TZ, probably USER and possibly LOGNAME; that may be all. If you're not sure, try running a crontab entry which captures the environment in a file:

* * * * * env > /tmp/cron.env

You're likely to find that the file is small. Don't forget to remove the entry after testing it.

One good thing that you're to be commended for:

  • Your script (a) ensures that it sets the environment, and (b) runs a simple command from the crontab entry, leaving the script to do the hard work.

In my view, the entries in the crontab file should indeed be simple like that, invoking a purpose-built script to do the real work.


Critique of Proposed Script in the Revised Question

#!/bin/bash
ORACLE_HOME="/opt/app/oracle/product/11.2.0/dbhome_1"
ORACLE_SID="HEER"
ORACLE_USER="USER1"
ORACLE_PASSWORD="USERPASS"

Thus far, no problem:

echo "export ORACLE_HOME=$ORACLE_HOME" >> $PWD/sqlcronprocedure.sh
echo "export PATH=\$ORACLE_HOME/bin:\$PATH" >> $PWD/sqlcronprocedure.sh
echo "export ORACLE_SID=$ORACLE_SID" >> $PWD/sqlcronprocedure.sh
echo "rTmpDir=/tmp" >> $PWD/sqlcronprocedure.sh

echo "sqlplus -s $ORACLE_USER@$ORACLE_SID/$ORACLE_PASSWORD  > $rTmpDir/deleteme.txt 2>&1 <<EOF" >> $PWD/sqlcronprocedure.sh
echo "    select 1 from dual;" >> $PWD/sqlcronprocedure.sh
echo "    execute prvsapupd(1000,14);" >> $PWD/sqlcronprocedure.sh
echo "EOF" >> $PWD/sqlcronprocedure.sh

This is horribly repetitive, and starting out with append is not good. I would use:

cronscript=$PWD/sqlcronprocedure.sh
{
echo "export ORACLE_HOME=$ORACLE_HOME"
echo "export PATH=\$ORACLE_HOME/bin:\$PATH"
echo "export ORACLE_SID=$ORACLE_SID"
echo "rTmpDir=/tmp"

echo "sqlplus -s $ORACLE_USER@$ORACLE_SID/$ORACLE_PASSWORD  > $rTmpDir/deleteme.txt 2>&1 <<EOF"
echo "    select 1 from dual;"
echo "    execute prvsapupd(1000,14);"
echo "EOF"
} > $cronscript

The { ... } apply the I/O redirection to the enclosed commands. Note that there must be a semi-colon or newline before the }.

chmod 755 $PWD/sqlcronprocedure.sh

Since I have a variable for the file name, I'd use it:

chmod 755 $cronscript

Then we have a problem with repetition here, plus not cleaning up behind ourselves:

crontab -l > $PWD/sqlcorn.sh
echo "0,15,30,45 * * * * $PWD/sqlcronprocedure.sh" >> $PWD/sqlcorn.sh
crontab $PWD/sqlcorn.sh

Thus I'd write:

crontab=sqlcron.sh
crontab -l > $crontab
echo "0,15,30,45 * * * * $cronscript" >> $crontab
crontab $crontab
rm -f $crontab

I still think that trap is not too hard and should be used in any script that creates temporary files; however, it's your mess, not mine. I'm not convinced the $PWD is needed everywhere; I left it in one name and not in the other. If you don't supply a directory path, the $PWD is implied. I also note that you're using a slightly different script name in your proposed full script from the one in the original. As long as the names are self-consistent, there isn't a problem (and using a variable helps ensure consistency), but be careful.

I'm not sure that I'd actually do it this way, but you could also avoid the temporary file using:

{
crontab -l
echo "0,15,30,45 * * * * $cronscript"
} | (sleep 1; crontab -)

This collects the current value and appends the extra line, feeding all that into a script that sleeps for a second (to allow the first part time to complete) before feeding the results back into crontab. There's a question of how reliable is the one second delay, mainly. It's likely fine, but not guaranteed. The temporary file is 100% reliable - I'd use it because it isn't any more complex. (I could use parentheses around the first pair of commands; I could use braces around the second pair of commands, but I'd need to add a semi-colon between the - and the ) that is replaced by }.)

Note that my original proposal was careful to ensure that even if the script was run multiple times, there'd be only one entry in the crontab file for the process. Your variants do not make sure of the idempotency.

like image 109
Jonathan Leffler Avatar answered Sep 20 '22 04:09

Jonathan Leffler