crontab not executing complex bash script - linux

SOLVED! add #!/bin/bash at the top of all my scripts in order to make use of bash extensions. Otherwise it restricts itself to POSIX shell syntax. Thanks Barmar!
Also, I'll add that I had trouble with gpg decryption not working from cronjob after I got it executing, and the answer was to add the --no-tty option (no terminal output) to the gpg command.
I am fairly new to linux, so bear with me...
I am able to execute a simple script with crontab -e when logged in as ubuntu:
* * * * * /ngage/extract/bin/echoer.sh
and this bash script simply prints output to a file:
echo "Hello" >> output.txt
But when I try to execute my more complex bash script in exactly the same way, it doesn't work:
* * * * * /ngage/extract/bin/superMasterExtract.sh
This script called into other bash scripts. There are 4 scripts in total, which 3 levels of hierarchy. It goes superMasterExtract > masterExtract > (decrypt, unzip)
Here is the code for superMasterExtract.sh (top level):
shopt -s nullglob # ignore empty file
cd /str/ftp
DIRECTORY='writeable'
for d in */ ; do # for all directories in /str/ftp
if [ -d "$d$DIRECTORY" ]; then # if the directory contains a folder called 'writeable'
files=($d$DIRECTORY/*)
dirs=($d$DIRECTORY/*/)
numdirs=${#dirs[#]}
numFiles=${#files[#]}
((numFiles-=$numdirs))
if [ $numFiles -gt 0 ]; then # if the folder has at least one file in it
bash /ngage/extract/bin/masterExtract.sh /str/ftp ${d:0:${#d} - 1} # execute this masterExtract bash script with two parameters passed in
fi
fi
done
masterExtract.sh:
DATE="$(date +"%m-%d-%Y_%T")"
LOG_FILENAME="log$DATE"
LOG_FILEPATH="/ngage/extract/logs/$2/$LOG_FILENAME"
echo "Log file is $LOG_FILEPATH"
bash /ngage/extract/bin/decrypt.sh $1 $2 $DATE
java -jar /ngage/extract/bin/sftp.jar $1 $2
bash /ngage/extract/bin/unzip.sh $1 $2 $DATE
java -jar /ngage/extract/bin/sftp.jar $1 $2
echo "Log file is $LOG_FILEPATH"
decrypt.sh:
shopt -s nullglob
UPLOAD_FILEPATH="$1/$2/writeable"
DECRYPT_FOLDER="$1/decryptedFiles/$2"
HISTORY_FOLDER="$1/encryptHistory/$2"
DONE_FOLDER="$1/doneFiles/$2"
LOG_FILENAME="log$3"
LOG_FILEPATH="/ngage/extract/logs/$2/$LOG_FILENAME"
echo "DECRYPT_FOLDER=$DECRYPT_FOLDER" >> $LOG_FILEPATH
echo "HISTORY_FOLDER=$HISTORY_FOLDER" >> $LOG_FILEPATH
cd $UPLOAD_FILEPATH
for FILE in *.gpg;
do
FILENAME=${FILE%.gpg}
echo ".done FILE NAME=$UPLOAD_FILEPATH/$FILENAME.done" >> $LOG_FILEPATH
if [[ -f $FILENAME.done ]]; then
echo "DECRYPTING FILE=$UPLOAD_FILEPATH/$FILE INTO $DECRYPT_FOLDER/$FILENAME" >> $LOG_FILEPATH
cat /ngage/extract/.sftpPasswd | gpg --passphrase-fd 0 --output "$DECRYPT_FOLDER/$FILENAME" --decrypt "$FILE"
mv $FILE $HISTORY_FOLDER/$FILE
echo "MOVING FILE=$UPLOAD_FILEPATH/$FILE INTO $HISTORY_FOLDER/$FILE" >> $LOG_FILEPATH
else
echo "Done file not found!" >> $LOG_FILEPATH
fi
done
cd $DECRYPT_FOLDER
for FILE in *
do
mv $FILE $DONE_FOLDER/$FILE
echo "DECRYPTED FILE=$DONE_FOLDER/$FILE" >> $LOG_FILEPATH
done
If anyone has a clue why it refuses to execute my more complicated script, I'd love to hear it. I have also tried setting some environment variables at the beginning of crontab as well:
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/local/bin:/usr/bin
MAILTO=jgardnerx85#gmail.com
HOME=/
* * * * * /ngage/extract/bin/superMasterExtract.sh
Note, I don't know that these are the appropriate variables for my installation or my script. I just pulled them off other posts and tried it to no avail. If these aren't the correct environment variables, can someone tell me how I can deduce the right ones for my particular application?

You need to begin your script with
#!/bin/bash
in order to make use of bash extensions. Otherwise it restricts itself to POSIX shell syntax.

Related

How to develop a Condition to close program only when log file has been updated in Bash Script [duplicate]

I want to run a shell script when a specific file or directory changes.
How can I easily do that?
You may try entr tool to run arbitrary commands when files change. Example for files:
$ ls -d * | entr sh -c 'make && make test'
or:
$ ls *.css *.html | entr reload-browser Firefox
or print Changed! when file file.txt is saved:
$ echo file.txt | entr echo Changed!
For directories use -d, but you've to use it in the loop, e.g.:
while true; do find path/ | entr -d echo Changed; done
or:
while true; do ls path/* | entr -pd echo Changed; done
I use this script to run a build script on changes in a directory tree:
#!/bin/bash -eu
DIRECTORY_TO_OBSERVE="js" # might want to change this
function block_for_change {
inotifywait --recursive \
--event modify,move,create,delete \
$DIRECTORY_TO_OBSERVE
}
BUILD_SCRIPT=build.sh # might want to change this too
function build {
bash $BUILD_SCRIPT
}
build
while block_for_change; do
build
done
Uses inotify-tools. Check inotifywait man page for how to customize what triggers the build.
Use inotify-tools.
The linked Github page has a number of examples; here is one of them.
#!/bin/sh
cwd=$(pwd)
inotifywait -mr \
--timefmt '%d/%m/%y %H:%M' --format '%T %w %f' \
-e close_write /tmp/test |
while read -r date time dir file; do
changed_abs=${dir}${file}
changed_rel=${changed_abs#"$cwd"/}
rsync --progress --relative -vrae 'ssh -p 22' "$changed_rel" \
usernam#example.com:/backup/root/dir && \
echo "At ${time} on ${date}, file $changed_abs was backed up via rsync" >&2
done
How about this script? Uses the 'stat' command to get the access time of a file and runs a command whenever there is a change in the access time (whenever file is accessed).
#!/bin/bash
while true
do
ATIME=`stat -c %Z /path/to/the/file.txt`
if [[ "$ATIME" != "$LTIME" ]]
then
echo "RUN COMMNAD"
LTIME=$ATIME
fi
sleep 5
done
Check out the kernel filesystem monitor daemon
http://freshmeat.net/projects/kfsmd/
Here's a how-to:
http://www.linux.com/archive/feature/124903
As mentioned, inotify-tools is probably the best idea. However, if you're programming for fun, you can try and earn hacker XPs by judicious application of tail -f .
Just for debugging purposes, when I write a shell script and want it to run on save, I use this:
#!/bin/bash
file="$1" # Name of file
command="${*:2}" # Command to run on change (takes rest of line)
t1="$(ls --full-time $file | awk '{ print $7 }')" # Get latest save time
while true
do
t2="$(ls --full-time $file | awk '{ print $7 }')" # Compare to new save time
if [ "$t1" != "$t2" ];then t1="$t2"; $command; fi # If different, run command
sleep 0.5
done
Run it as
run_on_save.sh myfile.sh ./myfile.sh arg1 arg2 arg3
Edit: Above tested on Ubuntu 12.04, for Mac OS, change the ls lines to:
"$(ls -lT $file | awk '{ print $8 }')"
Add the following to ~/.bashrc:
function react() {
if [ -z "$1" -o -z "$2" ]; then
echo "Usage: react <[./]file-to-watch> <[./]action> <to> <take>"
elif ! [ -r "$1" ]; then
echo "Can't react to $1, permission denied"
else
TARGET="$1"; shift
ACTION="$#"
while sleep 1; do
ATIME=$(stat -c %Z "$TARGET")
if [[ "$ATIME" != "${LTIME:-}" ]]; then
LTIME=$ATIME
$ACTION
fi
done
fi
}
Quick solution for fish shell users who wanna track a single file:
while true
set old_hash $hash
set hash (md5sum file_to_watch)
if [ $hash != $old_hash ]
command_to_execute
end
sleep 1
end
replace md5sum with md5 if on macos.
Here's another option: http://fileschanged.sourceforge.net/
See especially "example 4", which "monitors a directory and archives any new or changed files".
inotifywait can satisfy you.
Here is a common sample for it:
inotifywait -m /path -e create -e moved_to -e close_write | # -m is --monitor, -e is --event
while read path action file; do
if [[ "$file" =~ .*rst$ ]]; then # if suffix is '.rst'
echo ${path}${file} ': '${action} # execute your command
echo 'make html'
make html
fi
done
Suppose you want to run rake test every time you modify any ruby file ("*.rb") in app/ and test/ directories.
Just get the most recent modified time of the watched files and check every second if that time has changed.
Script code
t_ref=0; while true; do t_curr=$(find app/ test/ -type f -name "*.rb" -printf "%T+\n" | sort -r | head -n1); if [ $t_ref != $t_curr ]; then t_ref=$t_curr; rake test; fi; sleep 1; done
Benefits
You can run any command or script when the file changes.
It works between any filesystem and virtual machines (shared folders on VirtualBox using Vagrant); so you can use a text editor on your Macbook and run the tests on Ubuntu (virtual box), for example.
Warning
The -printf option works well on Ubuntu, but do not work in MacOS.

How to get the output of my script saved to a log

Hello this is purely practicing my cronjob
Here is my bash script below :
#!/bin/bash
grep $1 $2
rc=$?
if [ $rc != 0 ]
then
echo "specified string $1 not present in $2"
else
echo "specified string $1 is present in the file $2"
fi
# number of lines of in a file
wc -l $2 | awk '{print $1}'
Here is my crontab below :
20 16 * * * /home/mbarrett/Desktop/ ./find_string.sh sam text_string.file > /var/log/backupstring.log 2>&1
Any advice to what I may be doing wrong?
You have the Desktop directory at the beginning of your command, which will try to execute it as a program, which won't work. If you want to run the script from that directory, you need to execute the cd command to change to it.
20 16 * * * cd /home/mbarrett/Desktop/; ./find_string.sh sam text_string.file > /var/log/backupstring.log 2>&1
Also, get in the habit of always quoting your variables in the script. Your script as written won't work if the pattern or filename contains whitespace.

trouble getting incron inotify to work

so after alex answer here are my steps :
creating shell code
root#ip[/]# touch mylog.sh
root#ip[/]# nano mylog.sh
copying the code in the mylog.sh
#!/bin/bash
echo "File $1 created." >> /mylog.log
permission
root#ip[/]# chmod +x mylog.sh
creating the log file
root#ip[/]# touch mylog.log
opening icron table
incrontab -e
putting new command in
/test/ IN_CREATE mylog.sh $#$#
reloading incron - creating a new file - checking the log file
root#ip[/]# incrontab --reload
requesting table reload for user 'root'...
request done
root#ip[/]# cd test
root#ip[/test]# touch newfile.txt
root#ip[/test]# cd /
root#ip[/]# nano mylog.log
but still empty log file ... am i missing something ?
finally calling shell script with full path did the trick
so :
/test/ IN_CREATE /mylog.sh $#$#
You can usually find the incron logs in /var/log/messages
If you want to log events to a specific file you can use:
/test/ IN_CREATE mylog.sh $#$#
where mylog.sh is a shell script which handles the logging.
#!/bin/bash
echo "File $1 created." >> /home/myuser/filescreated.log
Don't forget to give execution permission to this shell script by chmod +x mylog.sh
Explanation:
As soon as you start using parameters for your command which you're calling, you have to put it all into a shell script. Since incron don't pass the arguments to your command but interprets it as an argument for itself.
Don't forget to call incrontab --reload after changing the incrontab.
Another example
incrontab -e
/text/ IN_CREATE /home/myuser/mylog.sh $# $#
mylog.sh
#!/bin/bash
echo "$(date) File $2 in $1 created." >> /home/myuser/log.txt
Following with Alexander's Baltasar answer, you could also have a script that does the redirection, and keep your end scripts free of that logic.
Below std_wrapper.sh:
#!/bin/bash
### FLAGS
set -Eeuo pipefail
### INIT SCRIPT
SCRIPT_FULLNAME=$(basename -- ${0})
usage="usage: ${SCRIPT_FULLNAME} log_file target_script target_file watched_dir event"
## ARGUMENTS
log_file="${1}"
target_script="${2}"
target_file="${3}"
watched_dir="${4}"
event="${5}"
### MAIN
if [ -z "${log_file}" ] || [ -z "${target_script}" ] || [ -z "${target_file}" ]; then
echo "${usage}" >&2
exit 1
fi
# do the actual call and apply the redirection:
${target_script} "${target_file}" "${watched_dir}" "${event}" >> "${log_file}" 2>&1
make sure the script can be run ($ chmod 770 std_wrapper.sh):
In your incrontab ($ incrontab -e):
/test/ IN_CREATE /path/std_wrapper.sh /path/log/test.create /path/actual_script.sh $# $# $%
actual_script.sh could look something like this:
#!/bin/bash
### FLAGS
set -Eeuo pipefail
### Input Parameters
filename="${1}"
watched_dir="${2}"
event="${3}"
full_filename="${watched_dir}${filename}"
### Main
dt="$(date '+%d/%m/%YT%H:%M:%S')"
echo "$dt (event:) $event (file:) $filename (dir:) $watched_dir <----- going to process ----->"
echo "sleeping 10 seconds..."
sleep 10
dt="$(date '+%d/%m/%YT%H:%M:%S')"
echo "$dt (event:) $event (full_filename:) $full_filename <----- returning from sleep -->"
Creating two files consecutively (in less than 10 seconds)
$ touch /test/new-file && sleep 5 && touch /test/another-file
Would create a log like this:
$ cat /path/log/test.create
07/11/2022T08:00:50 (event:) IN_CREATE (file:) new-file (dir:) /test/ <----- going to process ----->
sleeping 10 seconds...
07/11/2022T08:00:55 (event:) IN_CREATE (file:) another-file (dir:) /test/ <----- going to process ----->
sleeping 10 seconds...
07/11/2022T08:01:10 (event:) IN_CREATE (full_filename:) /test/new-file <----- returning from sleep -->
07/11/2022T08:01:15 (event:) IN_CREATE (full_filename:) /test/another-file <----- returning from sleep -->

linux zip and exclude dir via bash/shell script

I am trying to write a bash/shell script to zip up a specific folder and ignore certain sub-dirs in that folder.
This is the folder I am trying to zip "sync_test5":
My bash script generates an ignore list (based on) and calls the zip function like this:
#!/bin/bash
SYNC_WEB_ROOT_BASE_DIR="/home/www-data/public_html"
SYNC_WEB_ROOT_BACKUP_DIR="sync_test5"
SYNC_WEB_ROOT_IGNORE_DIR="dir_to_ignore dir2_to_ignore"
ignorelist=""
if [ "$SYNC_WEB_ROOT_IGNORE_DIR" != "" ];
then
for ignoredir in $SYNC_WEB_ROOT_IGNORE_DIR
do
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/**\*"
done
fi
FILE="$SYNC_BACKUP_DIR/$DATETIMENOW.website.zip"
cd $SYNC_WEB_ROOT_BASE_DIR;
zip -r $FILE $SYNC_WEB_ROOT_BACKUP_DIR -x $ignorelist >/dev/null
echo "Done"
Now this script runs without error, however it is not ignoring/excluding the dirs I've specified.
So, I had the shell script output the command it tried to run, which was:
zip -r 12-08-2014_072810.website.zip sync_test5 -x sync_test5/dir_to_ignore/**\* sync_test5/dir2_to_ignore/**\*
Now If I run the above command directly in putty like this, it works:
So, why doesn't my shell script exclude working as intended? the command that is being executed is identical (in shell and putty directly).
Because backslash quotings in a variable after word splitting are not evaluated.
If you have a='123\4', echo $a would give
123\4
But if you do it directly like echo 123\4, you'd get
1234
Clearly the arguments you pass with the variable and without the variables are different.
You probably just meant to not quote your argument with backslash:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/***"
Btw, what actual works is a non-evaluated glob pattern:
zip -r 12-08-2014_072810.website.zip sync_test5 -x 'sync_test5/dir_to_ignore/***' 'sync_test5/dir2_to_ignore/***'
You can verify this with
echo zip -r 12-08-2014_072810.website.zip sync_test5 -x sync_test5/dir_to_ignore/**\* sync_test5/dir2_to_ignore/**\*
And this is my suggestion:
#!/bin/bash
SYNC_WEB_ROOT_BASE_DIR="/home/www-data/public_html"
SYNC_WEB_ROOT_BACKUP_DIR="sync_test5"
SYNC_WEB_ROOT_IGNORE_DIR=("dir_to_ignore" "dir2_to_ignore")
IGNORE_LIST=()
if [[ -n $SYNC_WEB_ROOT_IGNORE_DIR ]]; then
for IGNORE_DIR in "${SYNC_WEB_ROOT_IGNORE_DIR[#]}"; do
IGNORE_LIST+=("$SYNC_WEB_ROOT_BACKUP_DIR/$IGNORE_DIR/***") ## "$SYNC_WEB_ROOT_BACKUP_DIR/$IGNORE_DIR/*" perhaps is enough?
done
fi
FILE="$SYNC_BACKUP_DIR/$DATETIMENOW.website.zip" ## Where is $SYNC_BACKUP_DIR set?
cd "$SYNC_WEB_ROOT_BASE_DIR";
zip -r "$FILE" "$SYNC_WEB_ROOT_BACKUP_DIR" -x "${IGNORE_LIST[#]}" >/dev/null
echo "Done"
This is what I ended up with:
#!/bin/bash
# This script zips a directory, excluding specified files, types and subdirectories.
# while zipping the directory it excludes hidden directories and certain file types
[[ "`/usr/bin/tty`" == "not a tty" ]] && . ~/.bash_profile
DIRECTORY=$(cd `dirname $0` && pwd)
if [[ -z $1 ]]; then
echo "Usage: managed_directory_compressor /your-directory/ zip-file-name"
else
DIRECTORY_TO_COMPRESS=${1%/}
ZIPPED_FILE="$2.zip"
COMPRESS_IGNORE_FILE=("\.git" "*.zip" "*.csv" "*.json" "gulpfile.js" "*.rb" "*.bak" "*.swp" "*.back" "*.merge" "*.txt" "*.sh" "bower_components" "node_modules")
COMPRESS_IGNORE_DIR=("bower_components" "node_modules")
IGNORE_LIST=("*/\.*" "\.* "\/\.*"")
if [[ -n $COMPRESS_IGNORE_FILE ]]; then
for IGNORE_FILES in "${COMPRESS_IGNORE_FILE[#]}"; do
IGNORE_LIST+=("$DIRECTORY_TO_COMPRESS/$IGNORE_FILES/*")
done
for IGNORE_DIR in "${COMPRESS_IGNORE_DIR[#]}"; do
IGNORE_LIST+=("$DIRECTORY_TO_COMPRESS/$IGNORE_DIR/")
done
fi
zip -r "$ZIPPED_FILE" "$DIRECTORY_TO_COMPRESS" -x "${IGNORE_LIST[#]}" # >/dev/null
# echo zip -r "$ZIPPED_FILE" "$DIRECTORY_TO_COMPRESS" -x "${IGNORE_LIST[#]}" # >/dev/null
echo $DIRECTORY_TO_COMPRESS "compressed as" $ZIPPED_FILE.
fi
After a few trial and error, I have managed to fix this problem by changing this line:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/**\*"
to:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/***"
Not sure why this worked, but it does :)

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
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.
I found a similar question: Edit crontab programmatically and force the daemon to refresh
Changing the permissions of the file is typical shell scripting with many resources available.
For the cronjob, you'll want to essentially interface with the crontab program and feed it and entirely new cron file. You can first retrieve already configured cron jobs, add yours to the list, and then call crontab again to give it the new input file.
See the man-page for crontab for more information.
#!/bin/sh
SCRIPT=/home/personal/scripts/cronSqlprocedure.sh
# write the script.
cat > $SCRIPT << 'EOF'
export ORACLE_HOME=/opt/app/oracle/product/11.2.0/dbhome_1
export PATH=$ORACLE_HOME/bin:$PATH
export ORACLE_SID=HEER
EOF
# Make the script executable
chmod +x $SCRIPT
# Add this script to the existing crontab. This relies on your
# sed supporting -i. if it does not, it is probably easiest to
# write a simple script that does the edit and set VISUAL to that script
if ! crontab -l | grep $SCRIPT > /dev/null; then
VISUAL='sed -i -e "\$a\
0,15,30,45 * * * * '$SCRIPT'"' crontab -e
fi
Create a cron.d file such as /etc/cron.d/my-sql-proc
0,15,30,45 * * * * root /home/personal/scripts/cronSqlprocedure.sh
#
# run-as user
Answer by #Patrick from unix.stackexchange.com:
I would recommend using /etc/cron.d over crontab.
You can place files in /etc/cron.d which behave like crontab entries. Though the format is slightly different.
Patrick points out it may not work on all systems, but his answer is both accepted and has the most votes. It works well for me on debian 9 (stretch).
source: https://unix.stackexchange.com/questions/117244/installing-crontab-using-bash-script#117254

Resources