Passing variables of bash script to another bash script for submitting job on queue - linux

I tried to read my file, the device (GPU or CPU), and the size of my vector from command line and then with respect to the chosen device, submit my job script with that parameters in to the queue of specific GPU or CPU. But after submitting my job to the queue, when I validate my script like:
bash job.sh
I saw that my parameters file, device, and size from submitjob.sh script did not pass to the job.sh script.
Could you please help me what is my mistake?!
My submitjob.sh:
#!/bin/sh -f
echo "Input The File!";
read file
echo "Choose Gpu Or Cpu!";
read device
echo "Input Vector Size!";
read size
echo
if [ "$device" = "Gpu" ]; then
qsub -I -l nodes=1:gen9:ppn=2 -d . ./job.sh file device size
else
qsub -I -l nodes=1:skl:ppn=2 -d . ./job.sh file device size
fi
my job.sh:
#!/bin/bash
source /opt/intel/inteloneapi/setvars.sh
echo
echo start: $(date "+%y%m%d.%H%M%S.%3N")
echo
$file $device $size
echo
echo stop: $(date "+%y%m%d.%H%M%S.%3N")
echo

You need to to put $ before the variables in submitjob.sh:
qsub -I -l nodes=1:gen9:ppn=2 -d . ./job.sh "$file" "$device" "$size"
Then in job.sh, you get the arguments from $1, $2, etc.
#!/bin/bash
source /opt/intel/inteloneapi/setvars.sh
file=$1
device=$2
size=$3
echo
echo start: $(date "+%y%m%d.%H%M%S.%3N")
echo
"$file" "$device" "$size"
echo
echo stop: $(date "+%y%m%d.%H%M%S.%3N")
echo

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.

Use of echo >> produces inconsistent results

I've been trying to understand a problem that's cropped up with some of the scripts we use at work.
To generate many of our script logs, we utilize the exec command and file redirects to print all output from the script to both the terminal and a log file. Occasionally, for information that doesn't need to be displayed to the user, we do a straight redirect to the log file.
The issue we're seeing occurs on the last line of output to the file when we're printing the number of errors that occurred during that execution: The text doesn't get printed to the file.
In an attempt to diagnose the problem, I wrote a simplified version of our production script (script1.bash) and a test script (script2.bash) to try to tease out the problem.
script1.bash
#!/bin/bash
log_name="${USER}_`date +"%Y%m%d-%H%M%S"`_${HOST}_${1}.log"
log="/tmp/${log_name}"
log_tmp="/tmp/temp_logs"
err_count=0
finish()
{
local ecode=0
if [ $# -eq 1 ]; then
ecode=${1}
fi
# This is the problem line
echo "Error Count: ${err_count}" >> "${log}"
mvlog
local success=$?
exec 1>&3 2>&4
if [ ${success} -ne 0 ]; then
echo ""
echo "WARNING: Failed to save log file to ${log_tmp}"
echo ""
ecode=$((ecode+1))
fi
exit ${ecode}
}
mvlog()
{
local ecode=1
if [ ! -d "${log_tmp}" ]; then
mkdir -p "${log_tmp}"
chmod 775 "${log_tmp}"
fi
if [ -d "${log_tmp}" ]; then
rsync -pt --bwlimit=4096 "${log}" "${log_tmp}/${log_name}" 2> /dev/null
[ $? -eq 0 ] && ecode=0
if [ ${ecode} -eq 0 ]; then
rm -f "${log}"
fi
fi
}
exec 3>&1 4>&2 >(tee "${log}") 2>&1
ecode=0
echo
echo "Some text"
echo
finish ${ecode}
script2.bash
#!/bin/bash
runs=10000
logdir="/tmp/temp_logs"
if [ -d "${logdir}" ]; then
rm -rf "${logdir}"
fi
for i in $(seq 1 ${runs}); do
echo "Conducting run #${i}/${runs}..."
${HOME}/bin/script1.bash ${i}
done
echo "Scanning logs from runs..."
total_count=`find "${logdir}" -type f -name "*.log*" | wc -l`
missing_count=`grep -L 'Error Count:' ${logdir}/*.log* | grep -c /`
echo "Number of runs performed: ${runs}"
echo "Number of log files generated: ${total_count}"
echo "Number of log files missing text: ${missing_count}"
My first test indicated roughly 1% of the time the line isn't written to the log file. I then proceeded to try several different methods of handling this line of output.
Echo and Wait
echo "Error Count: ${err_count}" >> "${log}"
wait
Alternate print method
printf "Error Count: %d\n" ${err_count} >> "${log}"
No Explicit File Redirection
echo "Error Count: ${err_count}"
Echo and Sleep
echo "Error Count: ${err_count}" >> "${log}"
sleep 0.2
Of these, #1 and #2 each had a 1% fail rate while #4 had a staggering 99% fail rate. #3 was the only methodology that had a 0% fail rate.
At this point, I'm at a loss for why this behavior is occurring, so I'm asking the gurus here for any insight.
(Note that the simple solution is to implement #3, but I want to know why this is happening.)
Without testing, this looks like a race condition between your script and tee. It's generally better to avoid multiple programs writing to the same file at the same time.
If you do insist on having multiple writers, make sure they are all in append mode, in this case by using tee -a. Appends to the local filesystem are atomic, so all writes should make it (this is not necessarily true for networked file systems).

Watch file to be updated [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.

Does editing a file while some script is performing grep in it has any effect

Well I am trying to take some decision based on some text is not present in a file, but the problem is the file will be modified while my shell script is doing grep in it.
#!/bin/bash
grep -q "decision" /home/tejto/test/testingshell
ret_code=$?
while [ $ret_code -ne 0 ]
do
echo $ret_code
grep -q "decision" /home/tejto/test/testingshell
echo 'Inside While!'
sleep 5
done
echo 'Gotcha!'
The text "decision" is not present in file while this shell script is started, but when I modify this file by some other bash prompt and put the text 'decision' in it, in this case my script is not taking that change and it is keep on looping in while , so does that mean my shell script caches that particular file ?
Because you are setting ret_code variable only once outside loop and not setting it again inside the loop after next grep -q command.
To fix you will need:
grep -q "decision" /home/tejto/test/testingshell
ret_code=$?
while [ $ret_code -ne 0 ]
do
echo $ret_code
grep -q "decision" /home/tejto/test/testingshell
ret_code=$?
echo 'Inside While!'
sleep 5
done
echo 'Gotcha!'
OR you can shorten your script like this:
#!/bin/bash
while ! grep -q "decision" /home/tejto/test/testingshell
do
echo $?
echo 'Inside While!'
sleep 5
done
echo 'Gotcha!'
i.e. no need to use a variable and directly use grep -q in your while condition.
[EDIT:Tejendra]
#!/bin/bash
until grep -q "decision" /home/tejto/test/testingshell
do
echo $?
echo 'Inside While!'
sleep 5
done
echo 'Gotcha!'
This last solution would not use ret_code and give the desired result as first solution.

BASH: redirect for log dillema / duplicate redirection for each loop iteration

I've got a redirect dilemma that I can't get past in a bash backup script I'm developing in CentOS 6.4. I want to redirect all output to two separate files: one tmp and one permanent. The script loops through an external source list and I'd like for the tmp log files to be specific to the source, so that I can send an email if that specific source had errors containing that log (and conversely remove the tmp if the backup completes without error).
I'm using exec to tee my output:
exec > >(tee -a ${templog} /var/log/rob/rob.log) 2>&1
This works if I place at the top of the script, but here the variable isn't defined yet, so I can't do source-specific logs.
If I place this within the while loop, it grabs the variable, but writes a copy of each line determined by the total iterations of the loop; for the example below, I have four sources it iterates through, so I get output for each source in quadruplicate:
-S-07/11/14 09:15:35 ROB-Source Process for cc2-gamma has started-S-
-S-07/11/14 09:15:35 ROB-Source Process for cc2-gamma has started-S-
-S-07/11/14 09:15:35 ROB-Source Process for cc2-gamma has started-S-
-S-07/11/14 09:15:35 ROB-Source Process for cc2-gamma has started-S-
Share cc2-gamma is not Mounted. Try 1 of 5 to mount...
Share cc2-gamma is not Mounted. Try 1 of 5 to mount...
Share cc2-gamma is not Mounted. Try 1 of 5 to mount...
Share cc2-gamma is not Mounted. Try 1 of 5 to mount...
Is there a different way to tee the output within the loop to prevent this (without touching each line of course)? Or is there something rotten in my loops that I'm not seeing? Here's the whole script. Please excuse the mess and style.. I'm clearly not finished. I didn't include the config.conf and backup source file as they don't affect the output. Let me know if needed. Thanks.
#!/bin/bash
#V.2014.0723 - Radation Oncology Backup script
#declarations
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/rob
source /rob/conf/config.conf
while read smbdir 'smbpath' exclfile drive foldername; do
#loop declarations
mountedfile=/rob/${smbdir}.MOUNTED
runningfile=/rob/${smbdir}.RUNNING
lastrunfile=/rob/${smbdir}_${foldername}.LASTRUN
templog=/rob/${smbdir}_${foldername}.TMPLOG
errorfile=/rob/${smbdir}_${foldername}.HAD_ERRORS
backupfile=/rob/${baname:0:3}_rtbackup.sql.bz2 # for the -l seccton below -- sql backup of backup.sql
#exec > >(tee -a ${templog} /var/log/rob/rob.log) 2>&1
### SOURCE BACKUP ##############################################################################################
if [ "$1" == "-s" ]
then
exec > >(tee -a /var/log/rob/rob.log ${templog}) 2>&1
#Write Source STDOUT and STDERR to both permanent and temporary log file. Must be in loop to use variables.
#exec > >(tee -a ${templog} /var/log/rob/rob.log) 2>&1
#exec > >(tee -a /var/log/rob.log ${templog}) 2>
if [ "${sources_active}" == "1" ]
then
echo "-S-$(date "+%m/%d/%y %T") ROB-Source Process for $smbdir has started-S-"
# unmount all cifs shares, due to duplicate mounts, write file to prevent concurrentcy
umount -a -t cifs > /dev/null
# The following will test to see if the souce is mounted, and if not, mount it.
for i in {1..5}
do
if mountpoint -q /mnt/${smbdir}/${drive}/${foldername}
then
echo "Share ${smbdir} is Mounted."
touch $mountedfile
break
else
sleep 2
echo "Share ${smbdir} is not Mounted. Try $i of 5 to mount..."
mkdir -p /mnt/${smbdir}/${drive}/${foldername} > /dev/null
mount -t cifs ${smbpath} -o ro,username=<USER>,password=<PW>,workgroup=<DOMAIN> /mnt/${smbdir}/${drive}/${foldername}
fi
done
# Test to see if above was successful, and if rob is not already running, run the backup.
if [[ -f ${mountedfile}&& ! -f ${runningfile} ]]
then
src="/mnt/${smbdir}/$drive"
dst="/backup/rob/"
touch ${runningfile}
/root/bin/rtbackup -m /mnt -p ${src}/${foldername} -b ${dst} -x #${exclfile}
if [ "$?" -ne "0" ]; then
#Errors Running RTBackup
rm -f ${runningfile} > /dev/null 2>&1
rm -f ${mountedfile}> /dev/null 2>&1
echo "$(date "+%m/%d/%y %T") Source Process for ${smbdir} had errors running:-SSS"
echo "$errors" >&2
touch ${errorfile}
exit 1
else
echo "What the hell is this doing?"
fi
#NO Errors Running RTBACKUP
rm -f ${templog}
rm -f ${runningfile} > /dev/null 2>&1
rm -f ${mountedfile} > /dev/null 2>&1
echo "$(date "+%m/%d/%y %T") Source Process for ${smbdir} did not have any errors"
else
#backup will *NOT* run, cleaning up and logging
rm -f ${mountedfile} > /dev/null 2>&1
echo "$(date "+%m/%d/%y %T") ${smbdir} could not be mounted, or is already in progress. Backup could not complete."
touch ${errorfile}
tail /var/log/rob/robso.log | mail -s "ROBSO Failed to run for ${smbdir} on ${baname}" ${email}
fi
echo "-F-$(date "+%m/%d/%y %T") ROB-Source Process for ${smbdir} has finished-F-"
#break
elif [[ "${sources_active}" == "0" ]]
then
echo "***$(date "+%m/%d/%y %T") ROB-Source Process for ${smbdir} did not run because the job is not set as active***"
#break
fi
done < /rob/conf/${baname}.conf
if [ $? -eq 10 ]; then exit 0; fi
You can use curly braces to redirect a set of commands; as it says in the bash manual about command grouping, "When commands are grouped, redirections may be applied to the entire command list". It behaves more-or-less like an anonymous function.
{
command1
command2
} > >(tee -a ${templog} /var/log/rob/rob.log) 2>&1
You can do the same with a named function, too, if you're so inclined, but I don't know offhand what environment would be used to expand the redirections. (If you do, please edit this answer!)
# Untested. This MIGHT work.
your_log_command() {
command1
command2
} > >(tee -a $1 /var/log/rob/rob.log) 2>&1
your_log_command $templog
your_log_command $something_else

Resources