unable to use if statement properly - bash

I am trying to use the simple conditional if statement as follows
val=-148.32
con=4.0
if [ $val > $con ]
then
echo 'akash'
else
echo 'mondal'
fi
in the above case I should get the output as mondal but I'm getting always akash. Can anyone plz let me know what mistake I'm doing?
Thank you.

The immediate problem is that a few of those backslashes are syntax errors.
The proper way to write this would be
val=-148.32
con=4.0
if [ $val -gt $con ]
then
echo 'akash'
else
echo 'mondal'
fi
though if you really wanted to, you could rewrite it as
val=-148.32;\
con=4.0;\
if [ $val -gt $con ];\
then\
echo 'akash';\
else\
echo 'mondal';\
fi
The ; separators are equivalent to newlines as statement separators (and the backslashes escape the actualy newline) -- as you can see, the separator is optional after then and else (but not before).
The > operator is supported by the [ (aka test) command, but it performs strict string comparison. The numeric comparison for greater-than is -gt.
The indentation is not syntactically important, but omitting it makes your code rather unreadable for human consumers.
However, as others have already remarked, Bash does not have any support for floating-point arithmetic. If you can rephrase your problem into an integer one, you should be able to use e.g.
val=-148.32
con=4.00
if [ ${val/.//} -gt ${con/.//} ]; then
echo 'akash'
else
echo 'mondal'
fi
but this requires the number of decimal points to be fixed, or at least predictable.
A common workaround is to use Awk instead.
val=-148.32
con=4.0
awk -v c="$con" '{ if ($1 > c) print "akash"; else print "mondal" }' <<<"$val"

I overcome the barrier in the following way
if awk "BEGIN {exit !($val > $con)}"
then
statement
else
statement
fi
where $val and $con are real variable

Related

Bash script with multiline variable

Here is my code
vmname="$1"
EXCEPTLIST="desktop-01|desktop-02|desktop-03|desktop-04"
if [[ $vmname != #(${EXCEPTLIST}) ]]; then
echo "${vmname}"
else
echo "Its in the exceptlist"
fi
The above code works perfectly but my question is , the EXCEPTLIST can be a long line, say 100 server names. In that case its hard to put all that names in one line. In that situation is there any way to make the variable EXCEPTLIST to be a multiline variable ? something like as follows:
EXCEPTLIST="desktop-01|desktop-02|desktop-03| \n
desktop-04|desktop-05|desktop-06| \n
desktop-07|desktop-08"
I am not sure but was thinking of possibilities.
Apparently I would like to know the terminology of using #(${})- Is this called variable expansion or what ? Does anyone know the documentation/explain to me about how this works in bash. ?
One can declare an array if the data/string is long/large. Use IFS and printf for the format string, something like:
#!/usr/bin/env bash
exceptlist=(
desktop-01
desktop-02
desktop-03
desktop-04
desktop-05
desktop-06
)
pattern=$(IFS='|'; printf '#(%s)' "${exceptlist[*]}")
[[ "$vmname" != $pattern ]] && echo good
In that situation is there any way to make the variable EXCEPTLIST to be a multiline variable ?
With your given input/data an array is also a best option, something like:
exceptlist=(
'desktop-01|desktop-02|desktop-03'
'desktop-04|desktop-05|desktop-06'
'desktop-07|desktop-08'
)
Check what is the value of $pattern variable one way is:
declare -p pattern
Output:
declare -- pattern="#(desktop-01|desktop-02|desktop-03|desktop-04|desktop-05|desktop-06)"
Need to test/check if $vmname is an empty string too, since it will always be true.
On a side note, don't use all upper case variables for purely internal purposes.
The $(...) is called Command Substitution.
See LESS=+'/\ *Command Substitution' man bash
In addition to what was mentioned in the comments about pattern matching
See LESS=+/'(pattern-list)' man bash
See LESS=+/' *\[\[ expression' man bash
s there any way to make the variable EXCEPTLIST to be a multiline variable ?
I see no reason to use matching. Use a bash array and just compare.
exceptlist=(
desktop-01
desktop-02
desktop-03
desktop-04
desktop-05
desktop-06
)
is_in_list() {
local i
for i in "${#:2}"; do
if [[ "$1" = "$i" ]]; then
return 0
fi
done
return 1
}
if is_in_list "$vmname" "${EXCEPTLIST[#]}"; then
echo "is in exception list ${vmname}"
fi
#(${})- Is this called variable expansion or what ? Does anyone know the documentation/explain to me about how this works in bash. ?
${var} is a variable expansion.
#(...) are just characters # ( ).
From man bash in Compund commands:
[[ expression ]]
When the == and != operators are used, the string to the right of the operator is considered a pattern and matched according to the rules
described below under Pattern Matching, as if the extglob shell option were enabled. ...
From Pattern Matching in man bash:
#(pattern-list)
Matches one of the given patterns
[[ command receives the #(a|b|c) string and then matches the arguments.
There is absolutely no need to use Bash specific regex or arrays and loop for a match, if using grep for raw string on word boundary.
The exception list can be multi-line, it will work as well:
#!/usr/bin/sh
exceptlist='
desktop-01|desktop-02|desktop-03|
deskop-04|desktop-05|desktop-06|
desktop-07|deskop-08'
if printf %s "$exceptlist" | grep -qwF "$1"; then
printf '%s is in the exceptlist\n' "$1"
fi
I wouldn't bother with multiple lines of text. This is would be just fine:
EXCEPTLIST='desktop-01|desktop-02|desktop-03|'
EXCEPTLIST+='desktop-04|desktop-05|desktop-06|'
EXCEPTLIST+='desktop-07|desktop-08'
The #(...) construct is called extended globbing pattern and what it does is an extension of what you probably already know -- wildcards:
VAR='foobar'
if [[ "$VAR" == fo?b* ]]; then
echo "Yes!"
else
echo "No!"
fi
A quick walkthrough on extended globbing examples: https://www.linuxjournal.com/content/bash-extended-globbing
#!/bin/bash
set +o posix
shopt -s extglob
vmname=$1
EXCEPTLIST=(
desktop-01 desktop-02 desktop-03
...
)
if IFS='|' eval '[[ ${vmname} == #(${EXCEPTLIST[*]}) ]]'; then
...
Here's one way to load a multiline string into a variable:
fn() {
cat <<EOF
desktop-01|desktop-02|desktop-03|
desktop-04|desktop-05|desktop-06|
desktop-07|desktop-08
EOF
}
exceptlist="$(fn)"
echo $exceptlist
As to solving your specific problem, I can think of a variety of approaches.
Solution 1, since all the desktop has the same desktop-0 prefix and only differ in the last letter, we can make use of {,} or {..} expansion as follows:
vmname="$1"
found=0
for d in desktop-{01..08}
do
if [[ "$vmname" == $d ]]; then
echo "It's in the exceptlist"
found=1
break
fi
done
if (( !found )); then
echo "Not found"
fi
Solution 2, sometimes, it is good to provide a list in a maintainable clear text list. We can use a while loop and iterate through the list
vmname="$1"
found=0
while IFS= read -r d
do
if [[ "$vmname" == $d ]]; then
echo "It's in the exceptlist"
found=1
break
fi
done <<EOF
desktop-01
desktop-02
desktop-03
desktop-04
desktop-05
desktop-06
desktop-07
desktop-08
EOF
if (( !found )); then
echo "Not found"
fi
Solution 3, we can desktop the servers using regular expressions:
vmname="$1"
if [[ "$vmname" =~ ^desktop-0[1-8]$ ]]; then
echo "It's in the exceptlist"
else
echo "Not found"
fi
Solution 4, we populate an array, then iterate through an array:
vmname="$1"
exceptlist=()
exceptlist+=(desktop-01 desktop-02 desktop-03 deskop-04)
exceptlist+=(desktop-05 desktop-06 desktop-07 deskop-08)
found=0
for d in ${exceptlist[#]}
do
if [[ "$vmname" == "$d" ]]; then
echo "It's in the exceptlist"
found=1
break;
fi
done
if (( !found )); then
echo "Not found"
fi

How to use if elif else in bash

I cannot figure out how to use a simple if/elif/else structure in bash. I cannot believe how something as trivial as that can be so unintuitive and difficult. I've already spent quite a bit of time fiddling around with that. What I want to do is something like that:
aaa="xxx"
if [[ $aaa -eq "bbb" ]]; then
echo "bbb"
elif [[ $aaa -eq "ccc" ]]; then
echo "ccc"
else
echo "something else"
fi
I've tried it with a single [, with two [[, with ((, with == instead of -eq, I'm really not a Linux guy and very confused about the syntax, I've seen all kinds of different syntaxes regardind if conditionals.
It always prints bbb, no matter what value aaa has.
Can somebody please explain to me how to do this so that it works?
-eq is for numeric comparison only, for more info consider reading:
Shell equality operators (=, ==, -eq)
Also, consider quoting the variables:
When to wrap quotes around a shell variable?
Changed -eq to ==:
Quoted the variables
#!/bin/bash
aaa="xxx"
if [[ "$aaa" == "bbb" ]]; then
echo "bbb"
elif [[ "$aaa" == "ccc" ]]; then
echo "ccc"
else
echo "something else"
fi
something else
Try it online!
Use -eq for numeric comparisons, not for strings. Also, you are using quotes incorrectly. Use double quotes to prevent field splitting when you expand variables. IOW, they are needed around variables, but not around literal strings (unless the literal string contains whitespace or characters that would be interpreted by the shell such as a backtick or a $, etc.). And don't use a string of if/else when a case statement is more appropriate. Overall:
#!/bin/sh
aaa="$1"
if [ "$aaa" = bbb ]; then
echo "bbb"
elif [ "$aaa" = ccc ]; then
echo "ccc"
else
echo "something else"
fi
case $aaa in
bbb) echo bbb;;
ccc) echo ccc;;
*) echo something else;;
esac
Regarding quotes: there is absolutely nothing wrong with using quotes as in if [ "$aaa" = "bbb" ]; or case "$aaa" in, but it is almost always a mistake to omit them as in if [ $aaa = "bbb" ] Omitting the quotes in case $aaa in or var=$aaa is allowed because field splitting does not happen in those cases, but it is certainly best practice to include the quotes in those cases. Generally, use quotes around varaibles. if [ $aaa = "bbb" ] is a huge source of potential bugs, and should be avoided.

Linux Regular Expression

I'm working with shell scripting in Linux. I want to check if the value of MAX_ARCHIVE_AGE is numeric or not. My code is like this:
MAX_ARCHIVE_AGE = "50"
expr="*[0-9]*"
if test -z "$MAX_ARCHIVE_AGE";
then
echo "MAX_ARCHIVE_AGE variable is missing or not initiated"
else
if [ "$MAX_ARCHIVE_AGE" != $expr ]
then
echo "$MAX_ARCHIVE_AGE is not a valid value"
fi
fi
I want to match the value of MAX_ARCHIVE_AGE with my expr. Please help.
For POSIX compatibility, look at case. I also find it more elegant than the corresponding if construct, but the syntax may seem a bit odd when you first see it.
case $MAX_ARCHIVE_AGE in
'' ) echo "empty" >&2 ;;
*[!0-9]* ) echo "not a number" >&2 ;;
esac
By the way, notice the redirection of error messages to standard error with >&2.
Your expr will match anything that contains any digits; it's better to check if it contains only digits, or conversely, to check if it contains any non-digits. To do that, you can write:
if ! [[ "$MAX_ARCHIVE_AGE" ]] ; then
echo "MAX_ARCHIVE_AGE is blank or uninitialized" >&2
elif [[ "$MAX_ARCHIVE_AGE" == *[^0-9]* ]] ; then
echo "$MAX_ARCHIVE_AGE is not a valid value" >&2
fi
Also, note that you would initialize MAX_ARCHIVE_AGE by writing e.g. MAX_ARCHIVE_AGE=50 (no spaces), not MAX_ARCHIVE_AGE = 50. The latter tries to run a program called MAX_ARCHIVE_AGE with the arguments = and 50.

unary operator expected in shell script when comparing null value with string

I have two variables
var=""
var1=abcd
Here is my shell script code
if [ $var == $var1 ]; then
do something
else
do something
fi
If I run this code it will prompt a warning
[: ==: unary operator expected
How can I solve this?
Since the value of $var is the empty string, this:
if [ $var == $var1 ]; then
expands to this:
if [ == abcd ]; then
which is a syntax error.
You need to quote the arguments:
if [ "$var" == "$var1" ]; then
You can also use = rather than ==; that's the original syntax, and it's a bit more portable.
If you're using bash, you can use the [[ syntax, which doesn't require the quotes:
if [[ $var = $var1 ]]; then
Even then, it doesn't hurt to quote the variable reference, and adding quotes:
if [[ "$var" = "$var1" ]]; then
might save a future reader a moment trying to remember whether [[ ... ]] requires them.
Why all people want to use '==' instead of simple '=' ? It is bad habit! It used only in [[ ]] expression. And in (( )) too. But you may use just = too! It work well in any case. If you use numbers, not strings use not parcing to strings and then compare like strings but compare numbers. like that
let -i i=5 # garantee that i is nubmber
test $i -eq 5 && echo "$i is equal 5" || echo "$i not equal 5"
It's match better and quicker. I'm expert in C/C++, Java, JavaScript. But if I use bash i never use '==' instead '='. Why you do so?

Arithmetic comparison in a shell

i want to compare two number values in a shell script (sh) but it doesn`t work:
#!/bin/sh
let a=30
let b=100
let x=$a-$b
echo $a $b $x
[ $a < $b ] && { echo ok; }
That outputs:
30 100 -70
./x: line 6: 100: No such file or directory
I believe that should be -lt (which stands for less than) rather than "<". "<" is for string comparisons.
Edit: Actually looking at this now it seems clear what the problem is. The "<" character does file redirection so that's what the shell is trying to do. You can escape that character by doing \< instead but as originally stated that will do string comparison rather than numeric comparison.
Replace < with -lt
Also, lose the "let." This isn't Basic. Bill Gates and Steve Ballmer (Developers!) have nothing to do with this universe.
"let" is perfectly fine in shell, and has nothing to do with M$ basic or what.!
#OP , you can use bc to compare numbers, especially if you are also comparing floats. See here for similar example
#!/bin/sh
let a=30
let b=100
let x=$a-$b
echo $a $b $x
if(($a < $b)) then
echo ok
fi

Resources