Why are my nested If conditions not working? - bash

Using bash to write this script. For some reason my second IF condition is not working. I never get the message "FALSE result" when the condition is false. BTW, I need to nest two more if conditions but wanted to get this first one working. Any suggestions?
if [[ condition == "true" ]] ; then
echo "TRUE result"
if [[ condition == "false" ]] ; then
echo "FALSE result"
fi
fi

There are two problems here. The first is that condition is a fixed string, and will never be equal to either "true" or "false". If it's supposed to be a variable, you need to use "$condition" to get its value (the $ is required to get the variable's value, the double-quotes are sometimes needed to avoid weird parsing of the value). So something like if [[ "$condition" == "true" ]] ; then.
The second problem is that since the second if is nested inside the first, it'll never be tested if the first condition is false. That is, if $condition is "false", it'd test whether it's equal to "true", and since it isn't it'll skip everything up to the last fi, and hence never compare it to "false".
What you probably want is an elif (short for "else if") clause instead of a nested if -- that way it'll make the second test only if the first fails, instead of only if it succeeds. Note that an elif clause is not nested, but an extension of the original if statement, so it doesn't take an additional fi to close it. So something like this:
if [[ "$condition" == "true" ]] ; then
echo "TRUE result"
elif [[ "$condition" == "false" ]] ; then
echo "FALSE result"
fi
If you're comparing a something against a list of possible strings/patterns, it might be better to use a case statement:
case "$condition" in
true)
echo "TRUE result" ;;
false)
echo "FALSE result" ;;
maybe)
echo "MAYBE result" ;;
*)
echo "Unrecognized result" ;;
esac

read -p "Enter hostname(s): " HOSTS
for host in $HOSTS
do
echo "Test ssh-port on $host"
nc -zv -w 2 $host 22
if [ $? -ne 0 ]; then
echo "$host is not reachable.. See message above. Check hostname/ssh-deamon/firewall"
continue
fi
if [ "$host" = "host1" -o "$host" = "host2" ] ; then
message=$(ssh -q -t adminuser#$host "/usr/bin/sudo systemctl is-active sc4s.service")
echo "The SC4S service on $host is $message"
[ "$message" = "inactive" ] && echo "Please run the startsplunk script to restart the service on $host"
fi
done

Related

Bash: Why does variable containing string a equate to string b in an if statement?

I set a prompt to get user input Yy/Nn. Then I check if the response matches Y/y or N/n. However everytime it evaluates to true on "$prompt_1"="Y"/"y" even when you answer N/n. Now I am sure there is a reason why, however searching around brought me to a few solutions IG: Qoutes around the variables. But nothing has helped to resolve this.
#!/bin/bash
clear;
echo "Would you like to proceed with installation?"
read prompt_1;
echo $prompt_1;
if [ "$prompt_1"="Y" ] || [ "$prompt_1"="y" ]; then
echo "You've accepted installation";
elif [ "$prompt_1”="N"] || [ “$prompt_1"="n" ]; then
exit;
fi
You need to add spaces around the = operator:
if [ "$prompt_1" = "Y" ] || [ "$prompt_1" = "y" ]; then
echo "You've accepted installation";
elif [ "$prompt_1" = "N" ] || [ "$prompt_1" = "n" ]; then
exit;
fi
Or using the pattern-matching operator =~ in a [[...]] expression:
if [[ "$prompt_1" =~ ^[Yy]$ ]]; then
echo "You've accepted installation";
elif [[ "$prompt_1" =~ ^[Nn]$ ]]; then
exit
fi
The case statement solution.
case $prompt_1 in
[Yy]) echo "You've entered $prompt_1 and accepted installation."
##: do something here
;;
[Nn]) echo "You entered $prompt_1 and did not accepted the installation."
exit
;;
*|'') echo "Unknown option ${prompt_1:-empty}." ##: not [Nn] or [Yy] or empty.
exit
;;
esac
That is not restricted to bash anymore, it should work on other POSIX sh shells too.
Another suggestion, using case and lower case substitution:
case ${prompt_1,,} in
y|yes|"just do it")
echo "You've accepted installation"
# perform installation
;;
n|no|forget) exit;;
*) echo "Wrong answer, try again";;
esac
PS: Works in bash4 or later.

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 Boolean testing

I am attempting to run a block of code if one flag is set to true and the other is set to false. ie
var1=true
var2=false
if [[ $var1 && ! $var2 ]]; then var2="something"; fi
Since that did not evaluate the way that I expected I wrote several other test cases and I am having a hard time understanding how they are being evaluated.
aa=true
bb=false
cc="python"
if [[ "$aa" ]]; then echo "Test0" ; fi
if [[ "$bb" ]]; then echo "Test0.1" ; fi
if [[ !"$aa" ]]; then echo "Test0.2" ; fi
if [[ ! "$aa" ]]; then echo "Test0.3" ; fi
if [[ "$aa" && ! "$bb" ]]; then echo "Test1" ; fi
if [[ "$aa" && ! "$aa" ]]; then echo "Test2" ; fi
if [[ "$aa" ]] && ! [[ "$bb" ]]; then echo "test3" ; fi
if [[ "$aa" ]] && ! [[ "$cc" ]]; then echo "test4" ; fi
if [[ $aa && ! $bb ]]; then echo "Test5" ; fi
if [[ $aa && ! $aa ]]; then echo "Test6" ; fi
if [[ $aa ]] && ! [[ $bb ]]; then echo "test7" ; fi
if [[ $aa ]] && ! [[ $cc ]]; then echo "test8" ; fi
When I run the preceding codeblock the only output I get is
Test0
Test0.1
Test0.2
however, my expectation is that I would get
Test0
Test1
Test3
Test5
Test7
I have tried to understand the best way to run similar tests, however most examples I have found are set up in the format of
if [[ "$aa" == true ]];
which is not quite what I want to do. So my question is what is the best way to make comparisons like this, and why do several of the test cases that I would expect to pass simply not?
Thank you!
Without any operators, [[ only checks if the variable is empty. If it is, then it is considered false, otherwise it is considered true. The contents of the variables do not matter.
Your understanding of booleans in shell context is incorrect.
var1=true
var2=false
Both the above variables are true since those are non-empty strings.
You could instead make use of arithmetic context:
$ a=1
$ b=0
$ ((a==1 && b==0)) && echo y
y
$ ((a==0 && b==0)) && echo y
$
$ ((a && !(b))) && echo y; # This seems to be analogous to what you were attempting
y
The shell does not have Boolean variables, per se. However, there are commands named true and false whose exit statuses are 0 and 1, respectively, and so can be used similarly to Boolean values.
var1=true
var2=false
if $var1 && ! $var2; then var2="something"; fi
The difference is that instead of testing if var1 is set to a true value, you expand it to the name of a command, which runs and succeeds. Likewise, var2 is expanded to a command name which runs and fails, but because it is prefixed with ! the exit status is inverted to indicate success.
(Note that unlike most programming languages, an exit status of 0 indicates success because while most commands have 1 way to succeed, there are many different ways they could fail, so different non-zero values can be assigned different meanings.)
true and false are evaluated as strings ;)
[[ $var ]] is an equivalent of [[ -n $var ]] that check if $var is empty or not.
Then, no need to quote your variables inside [[. See this reminder.
Finally, here is an explication of the difference between && inside brackets and outside.
The closest you can come seems to be use functions instead of variables because you can use their return status in conditionals.
$ var1() { return 0; }
$ var2() { return 1; } # !0 = failure ~ false
and we can test this way
$ var1 && echo "it's true" || echo "it's false"
it's true
$ var2 && echo "it's true" || echo "it's false"
it's false
or this way
$ if var1; then echo "it's true"; else echo "it's false"; fi
it's true
$ if var2; then echo "it's true"; else echo "it's false"; fi
it's false
Hope this helps.

Mimicking the Python "for-else" construct

Python has a handy language feature called "for-else" (similarly, "while-else"), which looks like this:
for obj in my_list:
if obj == target:
break
else: # note: this else is attached to the for, not the if
print "nothing matched", target, "in the list"
Essentially, the else is skipped if the loop breaks, but runs if the loop exited via a condition failure (for while) or the end of iteration (for for).
Is there a way to do this in bash? The closest I can think of is to use a flag variable:
flag=false
for i in x y z; do
if [ condition $i ]; then
flag=true
break
fi
done
if ! $flag; then
echo "nothing in the list fulfilled the condition"
fi
which is rather more verbose.
Using a subshell:
( for i in x y z; do
[ condition $i ] && echo "Condition $i true" && exit;
done ) && echo "Found a match" || echo "Didn't find a match"
You could put a sentinel value in the loop list:
for i in x y z 'end-of-loop'; do
if [ condition $i ]; then
# loop code goes here
break
fi
if [ $i == 'end-of-loop' ]; then
# your else code goes here
fi
done
Something very hacky to introduce similar syntax:
#!/bin/bash
shopt -s expand_aliases
alias for='_broken=0; for'
alias break='{ _broken=1; break; }'
alias forelse='done; while ((_broken==0)); do _broken=1;'
for x in a b c; do
[ "$x" = "$1" ] && break
forelse
echo "nothing matched"
done
 
$ ./t.sh a
$ ./t.sh d
nothing matched
You can do this but I personally find it hard to read:
while :;
do for i in x y z; do
if [[ condition ]]; then
# do something
break 2
done
echo Nothing matched the condition
break
done
I also enjoy devnull's answer, but this is even more pythonic:
for i in x y z; do
[ condition $i ] && break #and do stuff prior to break maybe?
done || echo "nothing matched"
This will only echo "nothing matched" if the loop did not break.
You can change this
if ! $flag; then
echo "nothing in the list fulfilled the condition"
fi
to something simpler like this
"$flag" || echo "nothing in the list fulfilled the condition"
if you only have one statement after it, although that's not really going to help much.

Shell script that asks user to continue with a y/n

I have a shell script that I want to ask the user if they want to continue. If they type 'n' and press enter the script will exit.
If they press 'y' and enter it will continue to run. I have this at the top of my script but it continues regardless of what I type.
What am I doing wrong ?
goon=
while [ -z $goon ]
do
echo -n 'Do you want to continue? '
read goon
if [[ $goon = 'n' ]]
then
break
fi
goon=
done
Use an infinity loop and case/esac like this:
while true
do
read -r -p 'Do you want to continue? ' choice
case "$choice" in
n|N) break;;
y|Y) echo 'Do your stuff here';;
*) echo 'Response not valid';;
esac
done
The 'break' statement will exit you out of your while loop.
If you want to exit the script you want to use 'exit'.
That works perfectly well for me if I get rid of the doubled square brackets:
if [ $goon = 'n' ]
Rather than echo + read, just use read -p
read -p "Do you want to continue? " goon
Here's a working example (== instead of = for equality testing)
goon=
while [ -z $goon ]
do
echo -n 'Do you want to continue? '
read goon
if [[ $goon == 'n' ]]
then
break
fi
goon=
done
Strange thing, the original works ok for me too ...
I'm less than positive, but it looks as though your if statement will always evaluate to false.
Here's a resource on BASH coding that explains how to use conditionals in the way you are attempting to.
http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-6.html#ss6.4
I think you mean "exit" instead of "break", if you want the whole script to exit.
Also, you aren't actually checking for "y", so it loops forever even if they do say "y".
if [[ $goon = 'n' ]]
then
exit
fi
if [[ $goon = 'y' ]]
then
break
fi
Try below script
#!/bin/bash
pause ()
{
REPLY=X
while [ "$REPLY" == "X" ] || [ "$REPLY" != "n" ]
do
echo -e "\t\tPress 'n' to continue\t\t\tPress 'x' to quit"
read -n1 -s
case "$REPLY" in
"x") exit ;;
"X") echo "case sensitive!!" ;;
"n") clear ;;
"N") echo "case sensitive!!" ;;
* ) echo "Invalid Option" ;;
esac
done
}
pause
echo "Hi"
am sure this script will give you only two options to move around...

Resources