Bash script start program and quit on given time - linux

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

Related

bash script exits with zero status even after kill signal

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.

Run 3 jobs sequantially

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!

Shell script to kill a process in specific time with process name and time as input

I need a shell script to kill a particular running process after a specific time by getting the process name and time as input.
I'm using centos machine i've tried the script but couldn't complete on killing the process on particular timing.
#!/bin/bash
read -p 'Process: ' name
read -p 'Timecontrol: ' time
ps -ef | grep $name | awk '{print $5}'
pkill -9 "$name"
the expected output to be kill the process in specific time which will be given as input.
With this script you can kill the running process at a specific time by giving the process name and the time.
[Note: The input for time must be in seconds only, i.e 120 for 2 minutes]
#!/bin/bash
LOG=/tmp/kill.log
EXIT_ON_KILL=true
read -p 'Process: ' name
read -p 'killat: ' time
PID=$(ps -ef | grep $name | awk '{print $2}')
ps -ef | grep $name | awk '{print $2}' &>>$LOG
if [ $? -eq 0 ]; then
echo -e "\n The process details "
ps -p $PID
else
echo -e "\nInvalid Process Name"
fi
current=$(date +"%T")
killat=$(date -d "+"$time" seconds" "+%T")
echo -e "\nCurrent time $current \nThe time target is $killat"
while :
do
current=$(date +"%T")
echo $current
if [ "${killat}" == "${current}" ]
then
kill -9 $PID &>>$LOG
if [ $? -eq 0 ]; then
echo "Process $name have been successfully killed"
if [ $EXIT_ON_KILL == true ];then
exit 0
fi
else
echo -e "\nFailed to Kill process $name"
echo -e "\nMay be Invalid Process Name"
exit 1
fi
fi
sleep 2
done
Sample Input:
Process: xxxx
Killat: 120
You can use a cron job to terminate the process in specific date and time.
If you have to use a script:
#!/bin/bash
read -p 'Process: ' name
read -p 'Timecontrol: ' timecontrol
while :
do
now="$(date --date="$(date +%H:%M)" +%s)"
x="$(date --date="$timecontrol" +%s)"
if [ $now == $x ]
then
ps -ef | grep $name | awk '{print $5}'
fi
done
Note:
Remove # if you plan to run this script forever.
The script will not kill the process at that particular time which you want to give it as an input as the script will run and die it will not wait for a specific time.
You can tickle this in two ways.
Loop but again it will run in the foreground
Cronjob
#!/bin/bash
EXIT_ON_KILL=true
read -p 'Process: ' name
read -p 'Timecontrol: ' timecontrol
while :
do
current_time=$(date +"%T")
echo $current_time "control "
current_time=$(echo $current_time | cut -d':' -f1-2)
if [ "${timecontrol}" == "${current_time}" ]
then
echo "killing process"
kill -9 $(ps -ef | grep $name | awk '{print $2}')
if [ $? -eq 0 ]; then
echo "Process killed having name $name and having $pid"
if [ $EXIT_ON_KILL == true ];then
exit 0
fi
else
echo "Failed to Kill process having name $name"
exit 1
fi
fi
sleep 2
done
awk '{print $2}' this return PID in ubuntu, you have to check-in Centos if it returns PID if not then change it to awk '{print $5}'
So you can run with
./kill_process.sh
Process: node
Timecontrol: 00:21
With Cron job, you do not need to pass time, just pass the name of the process and the script will run on the specified time and will kill the process.
#!/bin/bash
EXIT_ON_KILL=true
p_name=$1
kill -9 $(ps -ef | grep $p_name | awk '{print $2}')
if [ $? -eq 0 ]; then
echo "Process killed having name $p_name and having $pid"
if [ $EXIT_ON_KILL == true ];then
exit 0
fi
else
echo "Failed to Kill process having name $p_name"
exit 1
fi
Create-cron-job-on-CentOS
0 0 * * * /path_to_script/kill_process.sh node
This will kill process every-day-at-midnight

Shell function to tail a log file for a specific string for a specific time

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);<>;"

Functionality of a start-stop-restart shell script

I am a shell scripting newbie trying to understand some code, but there are some lines that are too complexe for me. The piece of code I'm talking about can be found here: https://gist.github.com/447191
It's purpose is to start, stop and restart a server. That's pretty standard stuff, so it's worth taking some time to understand it. I commented those lines where I am unsure about the meaning or that I completely don't understand, hoping that somone could give me some explanation.
#!/bin/bash
#
BASE=/tmp
PID=$BASE/app.pid
LOG=$BASE/app.log
ERROR=$BASE/app-error.log
PORT=11211
LISTEN_IP='0.0.0.0'
MEM_SIZE=4
CMD='memcached'
# Does this mean, that the COMMAND variable can adopt different values, depending on
# what is entered as parameter? "memcached" is chosen by default, port, ip address and
# memory size are options, but what is -v?
COMMAND="$CMD -p $PORT -l $LISTEN_IP -m $MEM_SIZE -v"
USR=user
status() {
echo
echo "==== Status"
if [ -f $PID ]
then
echo
echo "Pid file: $( cat $PID ) [$PID]"
echo
# ps -ef: Display uid, pid, parent pid, recent CPU usage, process start time,
# controling tty, elapsed CPU usage, and the associated command of all other processes
# that are owned by other users.
# The rest of this line I don't understand, especially grep -v grep
ps -ef | grep -v grep | grep $( cat $PID )
else
echo
echo "No Pid file"
fi
}
start() {
if [ -f $PID ]
then
echo
echo "Already started. PID: [$( cat $PID )]"
else
echo "==== Start"
# Lock file that indicates that no 2nd instance should be started
touch $PID
# COMMAND is called as background process and ignores SIGHUP signal, writes it's
# output to the LOG file.
if nohup $COMMAND >>$LOG 2>&1 &
# The pid of the last background is saved in the PID file
then echo $! >$PID
echo "Done."
echo "$(date '+%Y-%m-%d %X'): START" >>$LOG
else echo "Error... "
/bin/rm $PID
fi
fi
}
# I don't understand this function :-(
kill_cmd() {
SIGNAL=""; MSG="Killing "
while true
do
LIST=`ps -ef | grep -v grep | grep $CMD | grep -w $USR | awk '{print $2}'`
if [ "$LIST" ]
then
echo; echo "$MSG $LIST" ; echo
echo $LIST | xargs kill $SIGNAL
# Why this sleep command?
sleep 2
SIGNAL="-9" ; MSG="Killing $SIGNAL"
if [ -f $PID ]
then
/bin/rm $PID
fi
else
echo; echo "All killed..." ; echo
break
fi
done
}
stop() {
echo "==== Stop"
if [ -f $PID ]
then
if kill $( cat $PID )
then echo "Done."
echo "$(date '+%Y-%m-%d %X'): STOP" >>$LOG
fi
/bin/rm $PID
kill_cmd
else
echo "No pid file. Already stopped?"
fi
}
case "$1" in
'start')
start
;;
'stop')
stop
;;
'restart')
stop ; echo "Sleeping..."; sleep 1 ;
start
;;
'status')
status
;;
*)
echo
echo "Usage: $0 { start | stop | restart | status }"
echo
exit 1
;;
esac
exit 0
1)
COMMAND="$CMD -p $PORT -l $LISTEN_IP -m $MEM_SIZE -v" — -v in Unix tradition very often is a shortcut for --verbose. All those dollar signs are variable expansion (their text values are inserted into the string assigned to new variable COMMAND).
2)
ps -ef | grep -v grep | grep $( cat $PID ) - it's a pipe: ps redirects its output to grep which outputs to another grep and the end result is printed to the standard output.
grep -v grep means "take all lines that do not contain 'grep'" (grep itself is a process, so you need to exclude it from output of ps). $( $command ) is a way to run command and insert its standard output into this place of script (in this case: cat $PID will show contents of file with name $PID).
3) kill_cmd.
This function is an endless loop trying to kill the LIST of 'memcached' processes' PIDs. First, it tries to send TERM signal (politely asking each process in $LIST to quit, saving its work and shutting down correctly), gives them 2 seconds (sleep 2) to do their shutdown job and then tries to make sure that all processes are killed using signal KILL (-9), which slays the process immediately using OS facilities: if a process has not done its shutdown work in 2 seconds, it's considered hung). If slaying with kill -9 was successful, it removes the PID file and quits the loop.
ps -ef | grep -v grep | grep $CMD | grep -w $USR | awk '{print $2}' prints all PIDs of processes with name $CMD ('memcached') and user $USR ('user'). -w option of grep means 'the Whole word only' (this excludes situations where the sought name is a part of another process name, like 'fakememcached'). awk is a little interpreter most often used to take a word number N from every line of input (you can consider it a selector for a column of a text table). In this case, it prints every second word in ps output lines, that means every PID.
If you have any other questions, I'll add answers below.
Here is an explanation of the pieces of code you do not understand:
1.
# Does this mean, that the COMMAND variable can adopt different values, depending on
# what is entered as parameter? "memcached" is chosen by default, port, ip address and
# memory size are options, but what is -v?
COMMAND="$CMD -p $PORT -l $LISTEN_IP -m $MEM_SIZE -v"
In the man, near -v:
$ man memcached
...
-v Be verbose during the event loop; print out errors and warnings.
...
2.
# ps -ef: Display uid, pid, parent pid, recent CPU usage, process start time,
# controling tty, elapsed CPU usage, and the associated command of all other processes
# that are owned by other users.
# The rest of this line I don't understand, especially grep -v grep
ps -ef | grep -v grep | grep $( cat $PID )
Print all processes details (ps -ef), exclude the line with grep (grep -v grep) (since you are running grep it will display itself in the process list) and filter by the text found in the file named $PID (/tmp/app.pid) (grep $( cat $PID )).
3.
# I don't understand this function :-(
kill_cmd() {
SIGNAL=""; MSG="Killing "
while true
do
## create a list with all the pid numbers filtered by command (memcached) and user ($USR)
LIST=`ps -ef | grep -v grep | grep $CMD | grep -w $USR | awk '{print $2}'`
## if $LIST is not empty... proceed
if [ "$LIST" ]
then
echo; echo "$MSG $LIST" ; echo
## kill all the processes in the $LIST (xargs will get the list from the pipe and put it at the end of the kill command; something like this < kill $SIGNAL $LIST > )
echo $LIST | xargs kill $SIGNAL
# Why this sleep command?
## some processes might take one or two seconds to perish
sleep 2
SIGNAL="-9" ; MSG="Killing $SIGNAL"
## if the file $PID still exists, delete it
if [ -f $PID ]
then
/bin/rm $PID
fi
## if list is empty
else
echo; echo "All killed..." ; echo
## get out of the while loop
break
fi
done
}
This function will kill all the processes related to memcached slowly and painfully (actually quite the opposite).
Above are the explanations.

Resources