Waiting for a command to return in a bash script - bash

What I am trying to do:
My bash shell script contains a modprobe -a $modulename. Sometimes loading that module fails and the modprobe statement just gets stuck. It never returns and hence, my script is stuck too.
What I want to do is this: Call modprobe -a $modulename , wait for 20 secs and if the command does not return and script remains stuck for 20 secs, call that a failure and exit !
I am looking at possible options for that. I know timeout is one, which will allow me to timeout after certain time. So I am thinking :
timeout -t 10 modprobe -a $modulename
if [ "$?" -gt 0 ]; then
echo "error"
exit
fi
But the problem is $? can be > 0 , not just because of timeout, but because of an error while loading the module too and I want to handle the two cases differently.
Any ideas using timeout and without using timeout are welcome.

According to timeout(1), timeout exits with a specific code (124 in my case) if the command times out. It's highly unlikely that modprobe would exit with that code, so you could probably check specifically for that by changing your condition:
...
RET="$?"; if [[ "$RET" = "124" ]]; then echo timeout; OTHER COMMAND; elif [[ "$RET" -gt 0 ]]; then echo error; exit; fi
BTW, it is a very good practice to assign "$?" to a variable immediately after your command. You will avoid a lot of grief later...
If you really do need to make sure, you can check the modprobe source code to see what exit codes it produces, since apparently it was not deemed important enough to mention in its manual page...

consider using "expect", you can set a timeout as well as running different command depending on the outcome of the modprobe.
Regards,
Andrew.

Related

Handling of error : program run from shell doesnt return

I have a shell script which updates different firmwares with different executables.
I need to know if one of the executable has hung and not returning back to shell.
Can I introduce any kind of timeout ?
Sample shell script below. How to handle if updatefw command hangs and does not return.
#!/bin/sh
updatefw -c config.cfg
if [ $? != 0 ]; then
echo "exec1 failed"
exit 1
fi
exit 0
I suggest with timeout from GNU core utilities:
#!/bin/bash
timeout 30 updatefw -c config.cfg
if [[ $? == 124 ]]; then
echo "update failed"
exit 1
fi
When timeout quits updatefw, the return code is 124.
I assume here that the update will never take longer than 30 seconds.

Catching a specific error and re-trying script? - BASH

I have a bash script which runs a program to migrate some data. This fails around 30-40% of the time.
I want a way to retry the script when this particular error comes up, but I only want to try 3 times before failing.
The script outputs the following when it fails:
Error: The connection to the remote server has timed out, no changes have been committed. (#134 - scope: ajax_verify_connection_to_remote_site)
Edit: To be more specific....
migration.sh:
#!/bin/bash
various other scripts........
sudo a_broken_migration_program <Variables>
I want to retry broken_migration several times, ideally only when it fails with this specific error but if that's too complicated I will settle on retrying all errors.
To do this, just run your command in a loop:
#Loop until counter is 3
counter=1
while [[ $counter -le 3 ]] ; do
yourcommand && break
((counter++))
done
If yourcommand is successful then it will break the loop. If it's unsuccessful then it will increment the counter and loop. Until the counter is 3.
If you just want to retry on a specific error code, you could capture the error on failure, test the code, and increment:
#Loop until counter is 3
counter=1
while [[ $counter -le 3 ]]
do
#command to run
ssh person#compthatdoesntexist
rc=$?
[[ $rc -eq 255 ]] && ((counter++)) || break
done
This example tries to ssh to a box that doesn't exist. We then capture the return code $? in variable $rc. If $rc is 255 ("ssh: Could not resolve hostname compthatdoesntexist: Name or service not known") then it increments the counter and loops. Any other exit code kicks us out of the loop.

Building a killer script in bash

I've been trying to learn the syntax of logic statements in bash, how to do if/else, pipes and stuff. I'm trying to build a bash script, but I fail miserably after 3 hours of not getting how this stuff works.
Now I need this little script, I'll try to explain it using a generalized code, or call it whatever you want. Here you go:
while variable THRESHOLD = 10
{
if netstat -anltp contains a line with port 25565
then set variable THRESHOLD to 0 and variable PROCNUM to the process number,
else add 1 to variable THRESHOLD
sleep 5 seconds
}
kill the process No. PROCNUM
restart the script
Basically, what it does is, that once the socket closes, after a few tries, it kills the process which was listening on that port.
I'm pretty sure it's possible, but I can't figure out how to do it properly. Mostly because I don't understand pipes and am not really familiar with grep. Thank you for your help, in advance.
Don't want be offensive, but if you can write a "generalized" program all you need is learn th syntax of the while, if for bash and read the man pages of the grep and kill and so on...
And the pipes are the same as in your garden. Having two things: tap and pond. You can fill your pond with many ways (e.g. with rain). Also, you can open your tap getting water. But if you want fill the pond with the water from a tap, need a pipe. That's all. Syntax:
tap | pond
the output from a tap
connect with a pipe
to the (input) of the pond
e.g.
netstat | grep
the output from a netstat
connect with a pipe
to the input of the grep
that's all magic... :)
About the syntax: You tagged your question as bash.
So googling for a bash while syntax will show to you, this Beginners Bash guide
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_02.html
to, and you can read about the if in the same website.
Simply can't believe than after 3 hours you cannot understand basic while and if syntax to write your program with a bash syntax - especially, when you able write an "generalized" program...
is is not to hard (with modifying the 1st example in the above page) to write:
THRESHOLD="0"
while [ $THRESHOLD -lt 10 ]
do
#do the IF here
THRESHOLD=$[$THRESHOLD+1]
done
and so on...
#!/bin/bash
# write a little function
function do_error {
echo "$#" 1>&2
exit 1
}
# make the user pass in the path to the executable
if [ "$1" == "" ]; then
do_error "USAGE: `basename $0` <path to your executable>"
fi
if [ ! -e $1 ]; then
do_error "Unable to find executable at $1"
fi
if [ ! -x $1 ]; then
do_error "$1 is not an executable"
fi
PROC="$1"
PROCNAME=`basename $PROC`
# forever
while [ 1 ]; do
# check whether the process is up
proc=`ps -ef | grep $PROCNAME 2>/dev/null`
# if it is not up, start it in the background (unless it's a daemon)
if [ "$proc" == "" ]; then
$PROC &
fi
# reinitialize the threshold
threshold=0
# as long as we haven't tried 10 time, continue trying
while [ threshold -lt 10 ]; do
# run netstat, look for port 25565, and see if the connection is established.
# it would be better to checks to make sure
# that the process we expect is the one that established the connection
output=`netstat -anp | grep 25565 | grep ESTABLISHED 2>/dev/null`
# if netstat found something, then our process was able to establish the connection
if [ "$output" != "" ]; then
threshold = 0
else
# increment the threshold
threshold=$((threshold + 1))
fi
# i would sleep for one second
sleep 1
done
kill -9 $PROCNUM
done

how to assign a value to variable and get the return value of output in single line

I have below line in my script
script_list=`ssh#hostip ls -A /directory 2>/dev/null`
Is there a way to use that in if condition, so that i can get the script_list variable assigned or handle the failure scenario in else condition
Thanks in advance
You can simply check the automatic variable $? in the next line:
script_list=$( ssh ... )
rc=$?
if [[ $rc -ne 0 ]]; then
...something is wrong...
fi
This works because the exit code of ssh is the exit code of the command it ran remotely if ssh itself could be executed successfully. But usually, you don't care which part of the command chain failed, it's good enough to know that any part (the local ssh or the remote command failed).
No problem, just do it. An assignment is perfectly fine as a command (by command I mean the thing which can come after an if).
if asdf=$(echo test1; exit 1); then
echo "SUCCESS1: $asdf"
fi
if asdf=$(echo test0; exit 0); then
echo "SUCCESS0: $asdf"
fi

How can I wait for certain output from a process then continue in Bash?

I'm trying to write a bash script to do some stuff, start a process, wait for that process to say it's ready, and then do more stuff while that process continues to run. The issue I'm running into is finding a way to wait for that process to be ready before continuing, and allowing it to continue to run.
In my specific case I'm trying to setup a PPP connection. I need to wait until it has connected before I run the next command. I would also like to stop the script if PPP fails to connect. pppd prints to stdout.
In psuedo code what I want to do is:
[some stuff]
echo START
[set up the ppp connection]
pppd <options> /dev/ttyUSB0
while 1
if output of pppd contains "Script /etc/ppp/ipv6-up finished (pid ####), status = 0x0"
break
if output of pppd contains "Sending requests timed out"
exit 1
[more stuff, and pppd continues to run]
echo CONTINUING
Any ideas on how to do this?
I had to do something similar waiting for a line in /var/log/syslog to appear. This is what worked for me:
FILE_TO_WATCH=/var/log/syslog
SEARCH_PATTERN='file system mounted'
tail -f -n0 ${FILE_TO_WATCH} | grep -qe ${SEARCH_PATTERN}
if [ $? == 1 ]; then
echo "Search terminated without finding the pattern"
fi
It pipes all new lines appended to the watched file to grep and instructs grep to exit quietly as soon as the pattern is discovered. The following if statement detects if the 'wait' terminated without finding the pattern.
The quickest solution I came up with was to run pppd with nohup in the background and check the nobup.out file for stdout. It ended up something like this:
sudo nohup pppd [options] 2> /dev/null &
# check to see if it started correctly
PPP_RESULT="unknown"
while true; do
if [[ $PPP_RESULT != "unknown" ]]; then
break
fi
sleep 1
# read in the file containing the std out of the pppd command
# and look for the lines that tell us what happened
while read line; do
if [[ $line == Script\ /etc/ppp/ipv6-up\ finished* ]]; then
echo "pppd has been successfully started"
PPP_RESULT="success"
break
elif [[ $line == LCP:\ timeout\ sending\ Config-Requests ]]; then
echo "pppd was unable to connect"
PPP_RESULT="failed"
break
elif [[ $line == *is\ locked\ by\ pid* ]]; then
echo "pppd is already running and has locked the serial port."
PPP_RESULT="running"
break;
fi
done < <( sudo cat ./nohup.out )
done
There's a tool called "Expect" that does almost exactly what you want. More info: http://en.wikipedia.org/wiki/Expect
You might also take a look at the man pages for "chat", which is a pppd feature that does some of the stuff that expect can do.
If you go with expect, as #sblom advised, please check autoexpect.
You run what you need via autoexpect command and it will create expect script.
Check man page for examples.
Sorry for the late response but a simpler way would to use wait.
wait is a BASH built-in command which waits for a process to finish
Following is the excerpt from the MAN page.
wait [n ...]
Wait for each specified process and return its termination sta-
tus. Each n may be a process ID or a job specification; if a
job spec is given, all processes in that job's pipeline are
waited for. If n is not given, all currently active child pro-
cesses are waited for, and the return status is zero. If n
specifies a non-existent process or job, the return status is
127. Otherwise, the return status is the exit status of the
last process or job waited for.
For further reference on usage:
Refer to wiki page

Resources