why does pgrep fail in this process monitor? - bash

I have a monitor shell script that does effectively monitor and keep a process running. But it often fails in the sense that it starts a second, third or more instance of the process. I have also seen the pgrep command (pgrep -n -f wx_nanoserver) return the wrong pid at the command line...
Here's my script:
#!/bin/bash
check_process() {
# echo "$ts: checking $1"
[ "$1" = "" ] && return 0
[ `pgrep -n -f $1` ] && return 1 || return 0
}
while [ 1 ]; do
# timestamp
ts=`date +%T`
NOW=`date +"%Y%m%d-%H%M%S"`
# echo "$ts: begin checking..."
check_process "wx_nanoserver"
[ $? -eq 0 ] && echo "$ts: not running, restarting..." && `php /var/www/wx_nanoserver.php > /var/www/logs/wx_output_$NOW.log 2> /var/www/logs/wx_error_$NOW.log &`
sleep 5
done

try:
pgrep -n -f "$1" && return 1 || return 0
if you use [ ], you will try to check pgrep stdout data, and your script did not compare it with empty space or sth, without [ ], will using pgrep exit code.

Two weird things about your script:
[ `pgrep -n -f $1` ] && return 1 || return 0
works through side effects. The ``` part evaluates to either the pid of the process if found, or nothing if no process is found. The single[notation is a synonym for thetestbuiltin (or command on earlier systems) which happens to returntrueif its argument is a nonempty string andfalseif it is given no argument. So when a pid is found, the test becomes something like[ 1234 ]which evaluates to true and[ ]` otherwise, which evaluates to false. That is indeed what you want, but it would be cleaner to write:
pgrep -n -f "$1" &>/dev/null && return 1 || return 0
Another thing is
`php /var/www/wx_nanoserver.php > /var/www/logs/wx_output_$NOW.log 2> /var/www/logs/wx_error_$NOW.log &`
where you use command substitution for no apparent reason. You're asking bash to evaluate the output of your command rather than simply running it. As its output is redirected, it always evaluates to an empty string so it has no further effect. A side effect is that the command is run in a subshell, which is a good thing to deamonize it. Though, it would be cleaner to write:
( php /var/www/wx_nanoserver.php > /var/www/logs/wx_output_$NOW.log 2> /var/www/logs/wx_error_$NOW.log & )
Not sure though what the actual problem might be. Seems to be working that way anyhow.
Final note, the back tick `` notation has been deprecated in favour of the$()` notation.

Related

preserve bash return code through eval and function

I have a simple bash function, hle (highlight errors), that highlights important text when building. It is implemented as follows:
hle() (
set -o pipefail
"$#" 2>&1 |
sed -f $hldir/red.sed -f $hldir/blue.sed -f $hldir/orange.sed
)
so you could run hle make target, and all errors would appear to be red, warnings orange, and debug would be blue
Now, I have a script that does something like this:
foo.sh:
eval hle $command
[ $? ] && echo "SUCCESS"
But this doesn't work as $? represents the return code of the eval (which I believe is the return code of hle itself...) How would I preserve the return code from $command in my script?
As you found out, your implementation of hle should already result int the correct exit code. However, the test [ $? ] is flawed. With [ $? = 0 ] it should work as expected.
Nevertheless, here is an alternative implementation of hle that doesn't require changing set -o options and therefore doesn't need a sub shell too:
hle() {
"$#" 2>&1 |
sed -f "$hldir/red.sed" -f "$hldir/blue.sed" -f "$hldir/orange.sed"
return "${PIPESTATUS[0]}"
}
For eval, the exit status is that of the evaluated command, see bash's manual:
eval [arguments]
The arguments are concatenated together into a single command, which is then read and executed, and its exit status returned as the exit status of eval
By the way: Depending on $command you don't need eval at all. Right know I can only think of one case where eval would be important, namely pipes (command="cmd1 | cmd2"). However, in such a pipe only the first command would be executed by hle. If you really need the eval then put it inside the function ({ eval "$#"; } 2>&1 |).
Sorry, so after my conversatio nwith #Socowi, I discovered the problem wasn't with the eval or function, but rather with my test.
In the script, the return value of eval hle $command is in fact the same as the return value from command. My problem was with the [ $? ] && echo SUCCESS... $? resolves to either 1 or 0, which is always considered true within test. The proper line should have been:
eval hle $command
[ $? == 0 ] && echo "SUCCESS"

Bash script for searching a specific word in terminal output

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

How to return from sourced bash script automatically on any error?

I have a bash script which is only meant to used be when sourced.
I want to return from it automatically on any error, similar to what set -e does.
However setting set -e doesn't work for me because it will also exit the users shell.
Right now I'm handling returning manually like this command || return 1, for each command.
You can also use command || true or command || return.
If your requirement is something different, please update more precisely.
You can use trap. E.g.:
// foo.sh
function func() {
trap 'if [ $? -ne 0 ]; then echo "Trapped!"; return ; fi' DEBUG
echo 'foo'
find -name "foo" . 2> /dev/null
echo 'bar'
}
func
Two notes. First, the trap needs to be inside the function as shown. It won't work if it's just inside the script.
Two, there is a significant limitation. Even if you set the return to the trap (e.g., return 1), while func exists after the bad find command, $? is still zero, no matter what. I'm not sure if there's a way around that, so if it's important to preserve the exit value of the failed command, this may not work.
E.g., if you had:
func
func_return=$?
echo "return value is: $func_return"
func_return will always be zero. I've played around with trying to get the exit value of the failed command to pass out of the function trap and into the function exit value, but have not found a way to do it.
If you need to preserve the return value, you could update a global variable inside the debug trap.
If I understand well, you can set -e locally in each function.
cat sourced
f1 () {
local -
set -e
[ "$1" -eq "$1" ] 2> /dev/null && echo "$1"
}
cat script.sh
. sourced
param='bad'
ret=$(f1 "$param")
[ $? -eq 0 ] && echo "result = $ret" || \
echo "error in sourced file with param $param"
param=3
ret=$(f1 "$param")
[ $? -eq 0 ] && echo "result = $ret" || \
echo "error in sourced file with param $param"

Compound conditional in a bash while loop

Im modifying an existing bash script and having some trouble getting the while loop behaving correctly. This is the original code
while ! /usr/bin/executable1
do
# executable1 returned an error. So sleep for some time try again
sleep 2
done
I would like to change this to the following
while ! /usr/bin/executable1 && ! $(myfunc)
do
# executable1 and myfunc both were unsuccessful. So sleep for some time
sleep 2
done
executable1 returns 0 on success and 1 on failure. I understand that "true" in bash evaluates to 0 so thats why the original script would keep looping till executable returned success
Accordingly myfunc is coded like this
myfunc ()
{
# Check if file exists. If exits, return 0, If not, return 1
if [ -e someFile ]; then
return 0
fi
return 1
}
I notice that the my new while loop does not seem to call executable1. It always calls myfunc() and then exits out of the loop immediately. What am I doing wrong?
I tried various ways of coding the while loop (with (( )), [ ], [[ ]] etc), but nothing seems to fix it
You don't need $(...) to call a function, just to capture its standard output. You simply want
while ! /usr/bin/executable1 && ! myfunc
do
sleep 2
done
Note that myfunc can also be more simply written
myfunc () {
[ -e someFile ]
}
or even (in bash)
myfunc () [[ -e someFile ]]
Either way, it's almost not worth defining myfunc separately; just use
while ! /usr/bin/executable1 && ! [[ -e someFile ]]
do
sleep 2
done
It might also be simpler to use an until loop:
until /usr/bin/executable1 || [[ -e someFile ]]; do
sleep 2
done

BASH: How to correctly return true and false from a function

I have a function in a bash script that I want to return true if a process is running and false if not. The code for my function is:
checkProcess() {
if [ kill -s 0 $(getPid) > /dev/null 2>&1 ]; then
return 0
else
return 1
fi
}
When I use this function it does not seem to work. I use it like so:
if [ ! checkProcess ]; then
#do something, like rm file containing PID
fi
However if I just use the below implementation it works fine:
if [ ! kill -s 0 $(getPid) > /dev/null 2>&1 ]; then
#do something, like rm file containing PID
fi
Is there a reason for this? Am I not returning the correct values in my function? Or is it all just wrongly implemented?
[ is not syntax; it is a command. It is commonly used with if, which works based on its return code.
To directly check the return code of a different command, e.g. kill, you should remove the [ and ] (the last argument to [):
if kill -s 0 $(getPid) &>/dev/null; then
# ...
fi
Note that &> is a Bash extension, equivalent to >/dev/null 2>&1.

Resources