Daemon service state check. "$?" always returns 0 - bash

I have file "sleepdaemon" which launches python program as service
I run this script:
sudo /etc/init.d/sleepdaemon start
but when I check the state $?=0 always
this is how i check
if [ "$?" -eq 0 ]; then
echo "Process is running"
fi
if [ "$?" -eq 1 ]; then
echo "Process is not running"
fi
if [ "$?" -eq 2 ]; then
echo "Process is not running"
fi
What's the problem ?

The value of $? gets reset after every test, so you either need to save it in a variable before testing, or use a case ... esac statement.

sudo returns the result of the command, or 1 if it can't run it. ? is set for EVERY command:
if (( $? == 0 )); then
echo "Process is running"
elif (( $? == 1 )); then
echo "Process is not running" >&2
elif (( $? == 2 )); then
echo "Process is not running" >&2
else
echo "Unknown exit code $?" >&2
fi
Use (( ... )) for arithmetic comparisons - it is easier and safer. You should always have a default long-stop, and you should report errors to stderr, not stdout.
If $? is always zero, then check the documentation for sleepdaemon (I tried, but can't find a return code documented anywhere).

Related

How to make the script exit with exit code 1 but only after last iteration of the loop

I want to check if an application up and running by accessing the "health check" link, so I want to implement the following logic:
if it returns 200 the script stops working with exit code 0, but if it returns something else the script stops working with exit code 1, but I want to test the "health check" link 3 times in a row. So I did this
#!/bin/bash
n=0
until [ "$n" -ge 3 ]
do
i=`curl -o /dev/null -Isw '%{http_code}\n' localhost:8080`
if [ "$i" == "200" ]
then
echo "Health check is OK"
break
else
echo "Health check failed"
fi
n=$((n+1))
sleep 5
done
When it's 200 and break is triggered, it exits with exit code 0, which is fine, but I also want it to exit with exit code 1 if it's not 200 but after the third iteration of the until loop. If I add exit 1 inside or below the "if statement" the script exits with exit code 1 but after the first iteration.
Is there any way to make it exit with exit code 1 after the third iteration of the loop?
You could immediately exit 0 inside the loop when the health check passes, and exit 1 after the loop.
n=0
until [ "$n" -ge 3 ]
do
i=`curl -o /dev/null -Isw '%{http_code}\n' localhost:8080`
if [ "$i" == "200" ]
then
echo "Health check is OK"
exit 0
else
echo "Health check failed"
fi
n=$((n+1))
sleep 5
done
exit 1
Is there any way to make it exit with exit code 1 after the third iteration of the loop?
If we always want to exit 1 if any of the requests resulted in != 200, we can use a simple flag:
How can I declare and use Boolean variables in a shell script?
#!/bin/bash
n=0
failed=false
until [ "$n" -ge 3 ]
do
i=`curl -o /dev/null -Isw '%{http_code}\n' localhost:8080`
if [ "$i" == "200" ]
then
echo "Health check is OK"
else
echo "Health check failed"
failed=true
fi
n=$((n+1))
sleep 5
done
if [ "$failed" = true ] ; then
exit 1
else
exit 0
fi
Some additional tips to shorten the code:
We can remove the n variable by using a for loop
An short-if to exit
#!/bin/bash
failed=false
for (( i = 0; i < 3; i++ )); do
r=`curl -o /dev/null -Isw '%{http_code}\n' localhost:8080`
if [ "$r" == "200" ]; then
echo "Health check is OK"
else
echo "Health check failed"
failed=true
fi
sleep 5
done
[[ "$failed" = true ]] && exit 1 || exit 0
See #>>> notes:
#!/bin/bash
n=0
#>>> default status if never successful
status=1
until [ "$n" -ge 3 ]
do
i=`curl -o /dev/null -Isw '%{http_code}\n' localhost:8080`
if [ "$i" == "200" ]
then
echo "Health check is OK"
#>>> successful: set status to 0
status=0
break
else
echo "Health check failed"
fi
n=$((n+1))
sleep 5
done
#>>> exit with $status
exit $status
You don't need an extra boolean variable, if you set the status before the end of the loop. So move your counter update and sleep timer into the failed check branch:
#!/usr/bin/env sh
n=0
until [ "$n" -ge 3 ]; do
i=$(curl -o /dev/null -Isw '%{http_code}\n' localhost:8080)
if [ "$i" -eq 200 ]; then
echo "Health check is OK"
break
else
echo "Health check failed"
n=$((n + 1))
sleep 1
false # Set status to false
fi
done
#!/bin/sh
c=3
until
i=$(curl -Isw '%{http_code}' -o /dev/null localhost:8080)
[ "$i" = 200 ]
do
echo 'Health check failed' >&2
[ "$c" -gt 1 ] || exit
c=$((c-1))
sleep 5
done
echo 'Health check is OK' >&2
With the loop restructured this way (i.e. until <success> ...), the state of the counter variable can be used to exit after the last try. Having the counter start at n and decrease towards zero simplifies using and testing the variable. Also, sleep 5 after the nth failure is avoided.

Only run code if git tag exists for current commit in BASH [duplicate]

What would be the best way to check the exit status in an if statement in order to echo a specific output?
I'm thinking of it being:
if [ $? -eq 1 ]
then
echo "blah blah blah"
fi
The issue I am also having is that the exit statement is before the if statement simply because it has to have that exit code. Also, I know I'm doing something wrong since the exit would obviously exit the program.
Every command that runs has an exit status.
That check is looking at the exit status of the command that finished most recently before that line runs.
If you want your script to exit when that test returns true (the previous command failed) then you put exit 1 (or whatever) inside that if block after the echo.
That being said, if you are running the command and are wanting to test its output, using the following is often more straightforward.
if some_command; then
echo command returned true
else
echo command returned some error
fi
Or to turn that around use ! for negation
if ! some_command; then
echo command returned some error
else
echo command returned true
fi
Note though that neither of those cares what the error code is. If you know you only care about a specific error code then you need to check $? manually.
Note that exit codes != 0 are used to report errors. So, it's better to do:
retVal=$?
if [ $retVal -ne 0 ]; then
echo "Error"
fi
exit $retVal
instead of
# will fail for error codes == 1
retVal=$?
if [ $retVal -eq 1 ]; then
echo "Error"
fi
exit $retVal
An alternative to an explicit if statement
Minimally:
test $? -eq 0 || echo "something bad happened"
Complete:
EXITCODE=$?
test $EXITCODE -eq 0 && echo "something good happened" || echo "something bad happened";
exit $EXITCODE
$? is a parameter like any other. You can save its value to use before ultimately calling exit.
exit_status=$?
if [ $exit_status -eq 1 ]; then
echo "blah blah blah"
fi
exit $exit_status
For the record, if the script is run with set -e (or #!/bin/bash -e) and you therefore cannot check $? directly (since the script would terminate on any return code other than zero), but want to handle a specific code, #gboffis comment is great:
/some/command || error_code=$?
if [ "${error_code}" -eq 2 ]; then
...
Just to add to the helpful and detailed answer:
If you have to check the exit code explicitly, it is better to use the arithmetic operator, (( ... )), this way:
run_some_command
(($? != 0)) && { printf '%s\n' "Command exited with non-zero"; exit 1; }
Or, use a case statement:
run_some_command; ec=$? # grab the exit code into a variable so that it can
# be reused later, without the fear of being overwritten
case $ec in
0) ;;
1) printf '%s\n' "Command exited with non-zero"; exit 1;;
*) do_something_else;;
esac
Related answer about error handling in Bash:
Raise error in a Bash script
If you are writing a function – which is always preferred – you can propagate the error like this:
function()
{
if <command>; then
echo worked
else
return
fi
}
Now, the caller can do things like function && next as expected! This is useful if you have a lot of things to do in the if block, etc. (otherwise there are one-liners for this). It can easily be tested using the false command.
Using Z shell (zsh) you can simply use:
if [[ $(false)? -eq 1 ]]; then echo "yes" ;fi
When using Bash and set -e is on, you can use:
false || exit_code=$?
if [[ ${exit_code} -ne 0 ]]; then echo ${exit_code}; fi
This might only be useful in a limited set of use-cases, I use this specifically when I need to capture the output from a command and write it to a log file if the exit code reports that something went wrong.
RESULT=$(my_command_that_might_fail)
if (exit $?)
then
echo "everything went fine."
else
echo "ERROR: $RESULT" >> my_logfile.txt
fi
you can just add this if statement:
if [ $? -ne 0 ];
then
echo 'The previous command was not executed successfully';
fi
Below test scripts below work for
simple bash test commands
multiple test commands
bash test commands with pipe included:
if [[ $(echo -en "abc\n def" |grep -e "^abc") && ! $(echo -en "abc\n def" |grep -e "^def") ]] ; then
echo "pipe true"
else
echo "pipe false"
fi
if [[ $(echo -en "abc\n def" |grep -e "^abc") && $(echo -en "abc\n def" |grep -e "^def") ]] ; then
echo "pipe true"
else
echo "pipe false"
fi
The output is:
pipe true
pipe false

Not exiting from shell script

I am trying to stop processing a shell script with an IF condition.
output7=${output7##*[[:space:]]}
if [[ $output4 -gt 0 && $output5 -gt 0 && $output6 -gt 0 && $output7 -gt 0 ]]
then echo 'Success'
else echo 'Failure'|| exit 0
fi
echo 'Process Completed'
It is printing the following message but the next statement is also being executed. The output is:
Failure
Process Completed
Can anyone pls tell me why it is not exiting the script.
The problem here is that you are saying that "if echo 'Failure' fails, then exit 0", which is not what you want. You'll want
echo 'Failure'
exit 1
instead. Idiomatic Bash style for the whole section would be:
if [[ $output4 -gt 0 ]] && [[ $output5 -gt 0 ]] && [[ $output6 -gt 0 ]] && [[ $output7 -gt 0 ]]
then
echo 'Success'
else
echo 'Failure'
exit 1
fi

How can I perform an if/else using the shorthand form within a Bash string?

Without having to type out another block of if/else statements, how can I accomplish this within the string?
I want to avoid having to set the wording based on the boolean. The output from the script takes in cronjob_status as a boolean, which in turns does things based on the true/false value. I need to perserve the boolean that's being passed in via stdin for cronjob_status and I want to print the correct word.
if [ "${cronjob_status}" == True ]; then
cronjob_status_display = "enable"
else
cronjob_status_display = "disable"
fi
if [ $? -eq 0 ]
then
echo "Successfully ${cronjob_status_display} the cronjob"
else
echo "Could not create cronjob" >&2
fi
Add a short circuit evaluation, &&:
if [ $? -eq 0 ] && [ "${cronjob_status}" == True ]; then
echo "OK"
else
echo "NOT OK" >&2
fi
If you use the bash builtin [[, then this can also be written as :
if [[ $? -eq 0 && ${cronjob_status} == True ]]; then
echo "OK"
else
echo "NOT OK" >&2
fi
As a side note, you don't necessarily need to check the exit status with $?, you can run the command and check at once after that, for example if the command is e.g. /foo/bar, you could do:
if /foo/bar &>/dev/null && [[ ${cronjob_status} == True ]]; then
echo "OK"
else
echo "NOT OK" >&2
fi
You can use:
[[ $cronjob_status == True ]] &&
cronjob_status_display=enable || cronjob_status_display=disable
A one liner:
[[ $? -eq 0 ]] && echo "Successfully ${cronjob_status} the cronjob" || echo "Could not create cronjob" >&2

Bash : Check if PID exists with further logic

Question: How do you check if a PID exists and use the result within an IF statement in bash?
Things I've tried
if [ "$(ps -p $pid)" -eq 0 ]; then
echo "Running"
else
echo "Not Running"
fi
if [ "$(kill -0 $pid)" -eq 0 ]; then
echo "Running"
else
echo "Not Running"
fi
Neither of these evaluate correctly no matter how I redirect STDOUT/STDER
How do you check if a PID exists and use the result within an if statement?
You can capture the output in a variable and then check the exit status:
output=$(ps -p "$pid")
if [ "$?" -eq 0 ]; then
echo "Found"
echo "$output"
fi
Just remember that $? is getting reset every time you run a command, so something like the following wont work:
output=$(ps -p "$pid")
echo "$output"
# Now $? will be refering to the exit status of echo
if [ "$?" -eq 0 ]; then
echo "Found"
fi
One can also stick everything together in the if statement:
if output=$(ps -p "$pid")
then
echo "Found: $output"
fi
Make it dynamic by passing the pid you want to check:
#!/usr/local/bin/bash
if ps -p $1 > /dev/null;
then
echo "running"
else
echo "not running"
fi
Example runs:
What's your host OS?
If you have /proc then this may work for you:
if [ -d "/proc/$pid" ]; then
echo "Running"
else
echo "Not running"
fi

Resources