I keep getting zero status even after interrupting the script.
The first script
#!/bin/bash
## call the backup script
/usr/local/bin/backup 2>&1 >/dev/null
echo $?
backup
#!/bin/bash
exitscript() {
rm -f $LOCKFILE
echo "Script Status: $1 | tee -a ${LOG}"
echo "> End Date: $(date +'%d.%m.%Y %H:%M:%S')" | tee -a ${LOG}
exit $1
}
######START#######
trap "exitscript 1" 1 2 23 24 25
rsync ${args} ${src} ${dest} | tee -a ${RSYNC_LOG}
retcode=${PIPESTATUS[0]}
if [[ ${retcode} -ne 0 ]]; then
exitcode=1
fi
exitscript ${exitcode:-0}
When the First Script is run, it returns exit status of 0 although i have tried to kill the backup script before it ends (for that i have created a very large size file so that rsync takes time to copy the file and i get the time to kill the script before it ends)
ps -ef | grep -i backup
kill $PID
Another thing is that even after killing the backup script, rsync still runs. I would like for rsync to stop once the script is being killed and my first script to return the status code of zero.
Much appreciation for any suggestions. Thanks!
I assume the missing quote in echo "Script Status: $1 | tee -a ${LOG} is not relevant to the question.
When you want a function to handle the trap, you need to export that function.
And when you want to kill children, you should add these in your trap-function.
I tested these adjustments with a sleep command, it should work for rsync too.
#!/bin/bash
exitscript() {
echo "Script Status: $1"
(( $pleasekill > 0 )) && kill ${pleasekill}
echo "> End Date: $(date +'%d.%m.%Y %H:%M:%S')"
exit $1
}
# Export the function exitscript
export exitscript
######START#######
pleasekill=0
trap "exitscript 1" 1 2 23 24 25
# Start I/O-friendly rsync function
sleep 30 &
pleasekill=$!
wait
exitscript 2
When you test this with the first script, use ^C or kill -1 pid_of_backup.
Related
I am newbie to shell. I am trying to run 3 jobs sequentially. But I am not sure where I am going wrong. The jobs are not running in sequence. Please help on this.
Job 1
Job 2
Job 3
Here is the script to run the jobs:
#!/bin/bash
{
sync;
echo 3 > /proc/sys/vm/drop_caches
/data/Job1.sh
if [ `ps -ef | egrep 'awk' | egrep -v egrep | wc -l` -gt 1 ]]
/data/Job2.sh
if [[ `ps -ef | egrep 'awk' | egrep -v egrep | wc -l` -gt 1 ]];
/data/Job3.sh
fi
}
UPDATE:
#!/bin/sh
if [ $1 ]; then
NUM_PROC=$1
else
NUM_PROC=10
fi
for i in `seq 0 $((NUM_PROC-1))`; do
awk 'BEGIN {for(i=0;i<10000;i++)for(j=0;j<10000;j++);}' &
done
echo "PIDS: `pidof awk`"
**Shell script running: **scripts/job.sh 5****
You can make use of wait if you want to use it as a child process. Otherwise, remove the & after $job and the entire line wait $! as commented in the example.
#! /bin/bash
JOB_ARRAY=("/data/Job1.sh" "/data/Job2.sh" "/data/Job3.sh")
for job in "${JOB_ARRAY[#]}"; do
sync;
echo 3 > /proc/sys/vm/drop_caches;
echo "Starting job $job"
$job & # remove `&`
echo "Started job with pid $!"
wait $! # remove entire line
echo "Job finished, exit code is $?"
done
exit 0
EDIT:
In the JobX.sh scripts you'll have to wait until the child processes are finished. Append the following code to each JobX.sh script:
NUM_PROC=$1
[ ! -z "${NUM_PROC}" ] || NUM_PROC=10
for i in `seq 0 $((NUM_PROC-1))`; do
awk 'BEGIN {for(i=0;i<10000;i++)for(j=0;j<10000;j++);}' &
done
echo "PIDS: `pidof awk`"
# Wait for every child process to finish
wait
exit $?
p.s. you should always end your scripts with exit. Always enclose variables with "" if you use it in if, while, fir (etc) statements. And try to make functions instead of separate scripts:
#! /bin/bash
function _test_echo {
local message=$1
echo $message
}
_test_echo "Hello world!"
exit 0
Good luck scripting!
I am trying to check if a process is running with the code below:
SERVICE="./yowsup/yowsup-cli"
RESULT=`ps aux | grep $SERVICE`
if [ "${RESULT:-null}" = null ]; then
echo "not running"
else
echo "running"
fi
But it keeps echoing it is running although it is not. I realized that the grep itself comes as a result and that is the issue.
How can I skip the grep and just check for the process?
Use pgrep:
if pgrep "$SERVICE" >/dev/null 2>&1 ; then
echo "$SERVICE is running"
fi
or:
if pgrep -f "/path/to/$SERVICE" >/dev/null 2>&1 ; then
echo "$SERVICE is running"
fi
NOTE:
pgrep interprets its argument as a regular expression. As a result, paths containing regex characters will likely fail to match or produce false positives (e.g. pgrep -f /home/user/projects/c++/application/executable won't work as expected due to +). This issue can be worked around by escaping the characters in question (e.g. pgrep -f /home/user/projects/c\+\+/application/executable)
pgrep -f <pattern> matches the specified pattern against the whole command line of running processes. As a result, it will match paths appearing as arguments of other processes (e.g. run nano /usr/bin/sleep in one terminal and pgrep -f /usr/bin/sleep in another -> pgrep reports the pid of nano since it contains /usr/bin/sleep as an argument in its command line). To prevent these kind of false positives, prefix the pattern with a caret (^) to force pgrep to only match against the beginning of the command line (e.g. pgrep -f ^/usr/bin/sleep)
For systems where pgrep isn't available you can use:
service="[.]/yowsup/yowsup-cli"
if ps aux | grep -q "$service"; then
echo "not running"
else
echo "running"
fi
[.] in will force grep to not list itself as it won't match [.] regex.
grep -q can be utilized to avoid command substitution step.
Prefer using lowercase variables in shell.
The problem is that grep you call sometimes finds himself in a ps list, so it is good only when you check it interactively:
$ ps -ef | grep bash
...
myaut 19193 2332 0 17:28 pts/11 00:00:00 /bin/bash
myaut 19853 15963 0 19:10 pts/6 00:00:00 grep --color=auto bash
Easiest way to get it is to use pidof. It accepts both full path and executable name:
service="./yowsup/yowsup-cli" # or service="yowsup-cli"
if pidof "$service" >/dev/null; then
echo "not running"
else
echo "running"
fi
There is more powerful version of pidof -- pgrep.
However, if you start your program from a script, you may save it's PID to a file:
service="./yowsup/yowsup-cli"
pidfile="./yowsup/yowsup-cli.pid"
service &
pid=$!
echo $pid > $pidfile
And then check it with pgrep:
if pgrep -F "$pidfile" >/dev/null; then
echo "not running"
else
echo "running"
fi
This is common technique in /etc/init.d start scripts.
The following solution avoids issues with ps + grep, pgrep and pidof (see Advantages below):
# Check if process is running [$1: path to executable]
function is_process_running() {
local path="$1" line
while read -r line; do
[[ "${line}" == "${path}" || "${line}" == "${path} "* ]] && return 0
done < <(ps -e -o command=)
return 1
}
is_process_running "./yowsup/yowsup-cli" && echo "running" || echo "not running"
Explanation:
ps -e -o command= list all processes, only output command line of each process, omit header line
while read -r line; do ... done < <(ps ...) process output produced by ps line by line
[[ "${line}" == "${path}" || "${line}" == "${path} "* ]] check if line matches path exactly -or- path + space + argument(s)
Advantages:
Works for paths containing regex special characters that would trip grep without option -F or pgrep, e.g. /home/user/projects/c++/application/executable (see NOTE in this answer for details)
Avoids issues with ps + grep / pgrep reporting false positives if path appears as argument of some other process (e.g. nano /usr/bin/sleep + pgrep -f /usr/bin/sleep -> falsely reports pid of nano process)
Avoids issues with pidof reporting false positives for processes that are run from PATH (e.g. sleep 60s & + pidof /tmp/sleep -> falsely reports pid of sleep process running from /usr/bin/sleep, regardless of whether /tmp/sleep actually exists or not)
I thought pidof was made for this.
function isrunning()
{
pidof -s "$1" > /dev/null 2>&1
status=$?
if [[ "$status" -eq 0 ]]; then
echo 1
else
echo 0
fi
)
if [[ $(isrunning bash) -eq 1 ]]; then echo "bash is running"; fi
if [[ $(isrunning foo) -eq 1 ]]; then echo "foo is running"; fi
## bash
## function to check if a process is alive and running:
_isRunning() {
ps -o comm= -C "$1" 2>/dev/null | grep -x "$1" >/dev/null 2>&1
}
## example 1: checking if "gedit" is running
if _isRunning gedit; then
echo "gedit is running"
else
echo "gedit is not running"
fi
## example 2: start lxpanel if it is not there
if ! _isRunning lxpanel; then
lxpanel &
fi
## or
_isRunning lxpanel || (lxpanel &)
Note: pgrep -x lxpanel or pidof lxpanel still reports that lxpanel is running even when it is defunct (zombie); so to get alive-and-running process, we need to use ps and grep
current_pid="$$" # get current pid
# Looking for current pid. Don't save lines either grep or current_pid
isRunning=$(ps -fea | grep -i $current_pid | grep -v -e grep -e $current_pid)
# Check if this script is running
if [[ -n "$isRunning" ]]; then
echo "This script is already running."
fi
SERVICE="./yowsup/yowsup-cli"
RESULT=`ps aux | grep $SERVICE|grep -v grep`
if [ "${RESULT:-null}" = null ]; then
echo "not running"
else
echo "running"
fi
I'm trying to make a script that will run a program on parameter.
When the program has been executed for 1 minute the program will exit.
My code looks like this:
pid=$(pidof $1)
true=1
$1
while [ $true = 1 ]
do
time=$(ps -p $pid -o etime=)
if $time > 01:00
then
true=0
kill -9 $pid
echo "The process $pid has finish since the execution time has ended"
fi
done
Any ideas? Program lunches but does not quit.
Actually your problem is this line:
if $time > 01:00
As $time cannot be compared against 01:00
You need to first convert the time into seconds like this:
time=$(ps -p $pid -o etime= | awk -F ':' '{print $1*60 + $2}')
if [[ $time -gt 60 ]]; then
# your if block code here
fi
I need to the following things to make sure my application server is
Tail a log file for a specific string
Remain blocked until that string is printed
However if the string is not printed for about 20 mins quit and throw and exception message like "Server took more that 20 mins to be up"
If string is printed in the log file quit the loop and proceed.
Is there a way to include time outs in a while loop ?
#!/bin/bash
tail -f logfile | grep 'certain_word' | read -t 1200 dummy_var
[ $? -eq 0 ] && echo 'ok' || echo 'server not up'
This reads anything written to logfile, searches for certain_word, echos ok if all is good, otherwise after waiting 1200 seconds (20 minutes) it complains.
You can do it like this:
start_time=$(date +"%s")
while true
do
elapsed_time=$(($(date +"%s") - $start_time))
if [[ "$elapsed_time" -gt 1200 ]]; then
break
fi
sleep 1
if [[ $(grep -c "specific string" /path/to/log/file.log) -ge 1 ]]; then
break
fi
done
You can use signal handlers from shell scripts (see http://www.ibm.com/developerworks/aix/library/au-usingtraps/index.html).
Basically, you'd define a function to be called on, say, signal 17, then put a sub-script in the background that will send that signal at some later time:
timeout(pid) {
sleep 1200
kill -SIGUSR1 $pid
}
watch_for_input() {
tail -f file | grep item
}
trap 'echo "Not found"; exit' SIGUSR1
timeout($$) &
watch_for_input
Then if you reach 1200 seconds, your function is called and you can choose what to do (like signal your tail/grep combo that is watching for your pattern in order to kill it)
time=0
found=0
while [ $time -lt 1200 ]; do
out=$(tail logfile)
if [[ $out =~ specificString ]]; then
found=1
break;
fi
let time++
sleep 1
done
echo $found
The accepted answer doesn't work and will never exit (because althouth read -t exits, the prior pipe commands (tail -f | grep) will only be notified of read -t exit when they try to write to output, which never happens until the string matches).
A one-liner is probably feasible, but here are scripted (working) approaches.
Logic is the same for each one, they use kill to terminate the current script after the timeout.
Perl is probably more widely available than gawk/read -t
#!/bin/bash
FILE="$1"
MATCH="$2"
# Uses read -t, kill after timeout
#tail -f "$FILE" | grep "$MATCH" | (read -t 1 a ; kill $$)
# Uses gawk read timeout ability (not available in awk)
#tail -f "$FILE" | grep "$MATCH" | gawk "BEGIN {PROCINFO[\"/dev/stdin\", \"READ_TIMEOUT\"] = 1000;getline < \"/dev/stdin\"; system(\"kill $$\")}"
# Uses perl & alarm signal
#tail -f "$FILE" | grep "$MATCH" | perl -e "\$SIG{ALRM} = sub { `kill $$`;exit; };alarm(1);<>;"
I am calling another shell script testarg.sh within my main script.
the logfiles of testarg.sh are stored in $CUSTLOGS in the below format
testarg.DDMONYY.PID.log
example: testarg.09Jun10.21165.log
In the main script after the testarg process gets completed i need to grep the log file for the text "ERROR" and "COMPLETED SUCCESSFULLY".
How do i get the PID of the process and combine with DDMONYY for grepping. Also i need to check whether file
exists before grepping
$CUSTBIN/testarg.sh
$CUSTBIN/testarg.sh
rc=$?
if [ $rc -ne 0 ]; then
return $CODE_WARN
fi
You may background testarg.sh, which puts its pid into $!, and then wait for it:
#! /bin/bash
...
$CUSTBIN/testarg.sh &
LOGFILE=testarg.$(date +%d%b%y).$!.log # testarg.09Jun10.12345.log
wait $!
# ... $? is set as you expect ...
[ -f $LOGFILE ] && grep {pattern} $LOGFILE
...
If you can modify testarg.sh and it doesn't otherwise output anything, just change it to output its log file with a line like:
echo testarg.$(date +%blah).$$.log
then use:
fspec=$($CUSTBIN/testarg.sh)
in your parent.
Alternatively, you can provide a wrapper function to do the work:
#!/bin/bash
function fgpid() {
"$#" &
pid=$!
ps -ef | grep ${pid} | sed 's/^/DEBUG:/' >&2 # debugging
wait ${pid}
echo ${pid}
}
fspec=testarg.$(date +%d%b%y).$(fgpid sleep 5).log
echo ${fspec}
This produces:
pax> ./qq.sh
DEBUG:pax 2656 2992 con 15:27:00 /usr/bin/sleep
testarg.09Jun10.2656.log
as expected.
Or this if you think your executable may output something. This one stores the PID into a variable:
#!/bin/bash
function fgpid() {
"$#" &
pid=$!
ps -ef | grep ${pid} | sed 's/^/DEBUG:/' >&2 # debugging
wait ${pid}
}
fgpid sleep 5
fspec=testarg.$(date +%d%b%y).${pid}.log
echo ${fspec}
There are two simple ways to get the PID of some process you've just spawned.
One would be to modify the program being spawned (the subprocess) to have it write its PID to a file. You'd then read it therefrom with something like:
$CUSTBIN/testarg.sh
TSTARGSPID=$(cat /var/run/custbin.testarg.pid)
Another more elegant method would be:
$CUSTBIN/testarg.sh &
TSTARGSPID=$!
wait
# Do stuff with PID and output files