Bash script for searching a specific word in terminal output - bash

I'm trying to implement a bash script who supposed to search for a word in a Python script terminal output.
The Python script doesn't stop so "&" in the end of the command is needed but the "if [ $? == 0 ] ; then" condition doesn't work.
How it can be solved?
Thanks, Gal.
#!/bin/bash
#Check if Pixhawk is connected
PORT=/dev/ttyPixhawk
end=$((SECONDS+3))
not_exists=f
/usr/local/bin/mavproxy.py --daemon --non-interactive --master=$PORT | grep 'Failed' &> /dev/null &
while [ $SECONDS -lt $end ] ; do
if [ $? == 0 ] ; then
not_exists=t
fi
sleep 1
done
if [ $not_exists=t ] ; then
echo "Not Exists"
else
echo "Exists"
fi
kill $(pgrep -f '/usr/local/bin/mavproxy.py')

Bash doesn't know anything about the output of background commands. Check for yourself with [ 5444 -lt 3 ] & echo $?.
your if statement wouldn't work in any case because $? checks for the return value of the most recent previous command, which in this case is your while loop.
You have a few different options. If you're waiting for some output, and you know how long it is in the output until whatever target you're looking for occurs, you can have the python write to a file and keep checking on the file size with a timeout for failure.
You can also continue with a simple timed approach as you have where you just check the output after a few seconds and decide success or failure based on that.
You can make your python script actually end, or provide more error messages, or write only the relevant parts to file that way.
Furthermore, you really should run your script through shellcheck.net to notice more problems.
You'll need to define your goal and use case more clearly to get real help; all we can really say is "your approach will not work, but there are definitely approaches which will work"

You are checking the status of grep command output inside while loop using $?. This can be done if $? is the next command to be fired after grep and if grep is not a back-group process . But in your script, $? will return the status of while [$SECONDS -lt $end ]. You can try to re-direct the output to a temp file and check it's status
/usr/local/bin/mavproxy.py --daemon --non-interactive --master=$PORT | grep 'Failed' &> tmp.txt &
sleep 3
# If file exists and it's size is greater than 0, [ -s File] will return true
if [ -s tmp.txt ]; then
echo 'pattern exists'
else
echo 'pattern not exists'
fi

Related

PIPESTATUS[0] in BASH script

I am implementing a scenario in Unix Bash scripts. I have two scripts ABC.bash and XYZ.bash. There is one condition in ABC.bash when requester does not enter Y or y scripts exit with message and do not work further. ABC.bash working fine when runs alone.Problem arises when I run it from another bash script i.e. XYZ.bash. It does not check for exit condition. Syntax of logic in XYZ.bash.
echo "Calling ABC.bash from XYZ.bash"
ABC.bash $a $b | tee -a $LOGFILE; sleep 2
if [ ${PIPESTATUS[0]} = 0 ]
then
echo "Do some work"
else
echo "Check ABC.bash input"
exit 1
fi
But when ABC.bash $a $b exit with status 2 flow still goes to IF block rather than ELSE.In log I can see message as DEBUGMODE set to 0. I need this DEBUGMODE setting as it is required but want to exit if ABC.bash exit. Ideally it should go to ELSE part as ABC.bash exit with wrong user input.
Additionally I have set up DEBUGMODE option in XYZ.bash script. Like-
if [[ -z "$1" ]]
then
echo " ">> No input so default to 0"
DEBUGMODE=0
else
echo "DEBUGMODE set to $1"
DEBUGMODE=$1
fi
enter code here
The problem is that PIPESTATUS is a volatile variable. That is it will be reset as soon as any other command is executed. You need to remove the call to sleep 2 if you want to inspect the PIPESTATUS.
In your example, PIPESTATUS reflects the status of sleep 2. So replace
ABC.bash $a $b | tee -a $LOGFILE; sleep 2
if [ ${PIPESTATUS[0]} = 0 ]
by
ABC.bash $a $b | tee -a $LOGFILE; pstat=(${PIPESTATUS[#]}); sleep 2
if [ ${pstat[0]} = 0 ]
to save the status.
As best practice, unless the your code has full control over the any variable content, better to quote the variable. This will not prevent logical error (e.g, the extra sleep that modified the PIPESTATUS), but it will avoid accidental injection of code into the script (or unexpected syntax errors)
if [ "${PIPESTATUS[0]}" = 0 ] ; then

ps command in sh script not include the top command

I have written a script to check process is running or not,it work fine but while testing it, i have found that it not include top command count running in other terminal
check-process.sh
#!/bin/sh
OK=1
CRITICAL=0
PROCESS_NUM=$( ps -ef | grep $1 | grep -v "grep "|grep -v "sh"|wc -l )
#echo $PROCESS_NUM
if [ $PROCESS_NUM = $OK ]
then
echo "OK"
elif [ $PROCESS_NUM = $CRITICAL ]
then
echo "CRITICAL"
elif [ $PROCESS_NUM > $OK ]
then
echo "MULTIPLE process are runing"
else
echo "error"
fi
And i run top command in two terminals and run this script as follow:
./check-process.sh top
and out put is 0 CRITICAL , but when i run normal command ps -ef |grep -v "grep "| wc -l it gives two counts.
That mess of greps just has to go.
One "trick" for finding processes by name without finding your grep is to use a regular expression. That is, after all, what the Global Regular Expression Print command is for. You can use parameter expansion to construct a safe regular expression based on your input string, perhaps like this:
#!/bin/sh
if [ -z "$1" ]; then
echo "I'd love me an option." >&2
exit 1
fi
OK=1
CRITICAL=0
x="${1#?}" # make a temporary string missing the 1st chararcter,
re="[${1%$x}]$x" # then place the 1st character within square brackets.
PROC_COUNT=$( ps -ef | grep -w -c "$re" ) # yay, just one pipe.
if [ "$PROC_COUNT" -eq "$OK" ]; then
echo "OK"
elif [ "$PROC_COUNT" -eq "$CRITICAL" ]; then
echo "CRITICAL"
elif [ "$PROC_COUNT" -gt "$OK" ]; then
echo "MULTIPLE process are running"
else
echo "error"
fi
There are a few notable changes here:
I added something to fail with better explanation if no option is given.
The pipeline, of course. And the lines that create $re.
We're using -gt and -eq to test numeric values. man test for details.
I renamed your count variable to be clearer. What is a "PROCESS_NUM" really? Sounds like a PID to me.
All variables are quoted. I don't need to tell you why, you have the Google.
That said, you should also consider using pgrep instead of any sort of counting pipe, if it's available on your system. Try running pgrep and see what your OS tells you.

Bash test for command exit status

I've created a little rsync script to sync my podcasts. I've configured the script to email me when it's done. I am attempting to test rsync's exit status to decide which message gets sent. Here is the block:
my_command= rsync --log-file=/home/jake/logs/rsync.log -avzu $local_directory $remote_directory
if [ $? -eq 0 ]; then
$mail_expression_success
else
$mail_expression_fail
fi
No matter how the command finishes I get the message contained in the first variable. $mail_expression_success.
It's better just to do something like this:
if rsync ....
then
echo Yay
else
echo Oh noes
fi
What you want to do is this:
my_command=$(rsync --log-file=/home/jake/logs/rsync.log -avzu $local_directory $remote_directory)
if [ $? -eq 0 ]; then
$mail_expression_success
else
$mail_expression_fail
fi
You cannot have a space after =. If you put the space, your value is not properly assigned. You will be better off not assigning the output to f and just checking for the $?.

shell script to check if program should be running and restart it if appropriate

I have the following script
#!/bin/sh
if [cat stream_should_be_running.txt == 'true']; then #file will either contain true or false
if [ps ax|grep -v grep|grep tracker_stream]; then # check if stream is currently running
exit 0
else
/usr/local/bin/python2.7 ~/webapps/dashboard/fbadmin/manage.py tracker_stream; # restart stream
exit 0
else
exit 0
fi
This script should check if a daemon script is suppose to be running. If it is suppose to be running then it checks to see if the script is running and restarts it if it isn't. Currently I get syntax error: unexpected end of file when I try to running the file manually.
So two questions:
why am i pulling the syntax error
outside of this should this script run properly?
Thanks
EDIT:
here is an updated version of the script and a few notes:
#!/bin/sh
set -vx; # turn on shell debugging
if [[ "$(cat stream_should_be_running.txt)" == "true" ]]; then
if [ ps ax|grep -v grep|grep -q tracker_stream ]; then
exit 0
else
/usr/local/bin/python2.7 ~/webapps/dashboard/fbadmin/manage.py tracker_stream;
exit 0
fi
else
exit 0
fi
to note:
vim marks $(...) as a syntax error ( i don't know if that matters)
ps ax|grep -v grep|grep -q tracker_stream , ps ax|grep -v grep|grep tracker_stream , and cat stream_should_be_running.txt all execute properly from the command line
EDIT 2:
shell debugging gives the error
$ sh stream_checker.sh
+ $'\r'
: command not foundline 3:
if [[ "$(cat stream_should_be_running.txt)" == "true" ]]; then
echo 'test';
if [ ps ax|grep -v grep|grep -q tracker_stream ]; then
exit 0
else
/usr/local/bin/python2.7 ~/webapps/dashboard/fbadmin/manage.py tracker_stream;
exit 0
fi
else
exit 0
fi
stream_checker.sh: line 15: syntax error: unexpected end of file
so the only things that come before where the + $'\r' is returns are #!/bin/sh and set -vx.
This is running on a linux system. The my local machine is osx lion and the live machine is a linux server on webfaction.
1) I think I got it...
I used the '-s' switch in pidof to only get one result.
The '-z' switch means "return true if the string is empty".
#!/bin/sh
PID=$(pidof -s tracker_stream);
if [ $(cat stream_should_be_running.txt) = "true"]; then #file will either contain true or false
if [ -z $PID ]; then # check if stream is currently NOT running
/usr/local/bin/python2.7 ~/webapps/dashboard/fbadmin/manage.py tracker_stream; # restart stream
exit 0;
fi
fi
EDIT: From the last note you posted it looks like you might have a Ctrl-M char (^M), somewhere on your file.
It's not merely a ^ followed by a M, it's a end of line character.
You could open your file with vim -b to check if you see any of those characters.
Then type:
:%s/^V^M//g
That command reads like "match all (^M) chars and substitute them with void".
In short it will remove all (^M) chars from your file.
The (^V^M) bit means that you have to hit CTRL-V CTRL-M, in order to insert the (^M) char.
2) what exactly do you mean by "outside of this"?
your square brackets need spaces around them, i.e.
if [ ps ax|grep -v grep|grep tracker_stream ] ;
AND more importantly, you need to use command substitution so your script can get the value inside stream_should_be_running.txt using $( cat ... ), i.e.
if [[ "$(cat stream_should_be_running.txt)" == 'true' ]] ; then
#file will either contain true or false
if[ ps ax|grep -v grep|grep -q tracker_stream ] ; then
# check if stream is currently running
exit 0
else
/usr/local/bin/python2.7 ~/webapps/dashboard/fbadmin/manage.py tracker_stream;
# restart stream
exit 0
else
exit 0
fi
Also better to dbl-quote the value returned by $(cat ...) in case some how there are spaces in the file.
Finally, turn on shell debugging by adding set -vx near top script. Then you can each line/block of code as it being executed, AND the values that are substituted for variables.
I hope this helps.

bash script: how to save return value of first command in a pipeline?

Bash: I want to run a command and pipe the results through some filter, but if the command fails, I want to return the command's error value, not the boring return value of the filter:
E.g.:
if !(cool_command | output_filter); then handle_the_error; fi
Or:
set -e
cool_command | output_filter
In either case it's the return value of cool_command that I care about -- for the 'if' condition in the first case, or to exit the script in the second case.
Is there some clean idiom for doing this?
Use the PIPESTATUS builtin variable.
From man bash:
PIPESTATUS
An array variable (see Arrays
below) containing a list of exit
status values from the processes in
the most-recently-executed foreground
pipeline (which may contain only a
single command).
If you didn't need to display the error output of the command, you could do something like
if ! echo | mysql $dbcreds mysql; then
error "Could not connect to MySQL. Did you forget to add '--db-user=' or '--db-password='?"
die "Check your credentials or ensure server is running with /etc/init.d/mysqld status"
fi
In the example, error and die are defined functions. elsewhere in the script. $dbcreds is also defined, though this is built from command line options. If there is no error generated by the command, nothing is returned. If an error occurs, text will be returned by this particular command.
Correct me if I'm wrong, but I get the impression you're really looking to do something a little more convoluted than
[ `id -u` -eq '0' ] || die "Must be run as root!"
where you actually grab the user ID prior to the if statement, and then perform the test. Doing it this way, you could then display the result if you choose. This would be
UID=`id -u`
if [ $UID -eq '0' ]; then
echo "User is root"
else
echo "User is not root"
exit 1 ##set an exit code higher than 0 if you're exiting because of an error
fi
The following script uses a fifo to filter the output in a separate process. This has the following advantages over the other answers. First, it is not bash specific. In particular it does not rely on PIPESTATUS. Second, output is not stalled until the command has completed.
$ cat >test_filter.sh <<EOF
#!/bin/sh
cmd()
{
echo $1
echo $2 >&2
return $3
}
filter()
{
while read line
do
echo "... $line"
done
}
tmpdir=$(mktemp -d)
fifo="$tmpdir"/out
mkfifo "$fifo"
filter <"$fifo" &
pid=$!
cmd a b 10 >"$fifo" 2>&1
ret=$?
wait $pid
echo exit code: $ret
rm -f "$fifo"
rmdir "$tmpdir"
EOF
$ sh ./test_filter.sh
... a
... b
exit code: 10

Resources