How to compare in shell script? - linux

How to compare in shell script?
Or, why the following script prints nothing?
x=1
if[ $x = 1 ] then echo "ok" else echo "no" fi

With numbers, use -eq, -ne, ... for equals, not equals, ...
x=1
if [ $x -eq 1 ]
then
echo "ok"
else
echo "no"
fi
And for others, use == not =.

Short solution with shortcut AND and OR:
x=1
(( $x == 1 )) && echo "ok" || echo "no"

You could compare in shell in two methods
Single-bracket syntax ( if [ ] )
Double-parenthesis syntax ( if (( )))
Using Single-bracket syntax
Operators :-
-eq is equal to
-ne is not equal to
-gt is greater than
-ge is greater than or equal to
-lt is less than
-le is less than or equal to
In Your case :-
x=1
if [ $x -eq 1 ]
then
echo "ok"
else
echo "no"
fi
Double-parenthesis syntax
Double-parentheses construct is also a mechanism for allowing C-style manipulation of variables in Bash, for example, (( var++ )).
In your case :-
x=1
if (( $x == 1 )) # C like statements
then
echo "ok"
else
echo "no"
fi

It depends on the language. With bash, you can use == operator. Otherwize you may use -eq -lt -gt for equals, lowerthan, greaterthan.
$ x=1
$ if [ "$x" == "2" ]; then echo "yes"; else echo "no"; fi
no
Edit: added spaces arround == and tested with 2.

Related

Bash script outputs wrong answer [duplicate]

I'm unable to get numeric comparisons working:
echo "enter two numbers";
read a b;
echo "a=$a";
echo "b=$b";
if [ $a \> $b ];
then
echo "a is greater than b";
else
echo "b is greater than a";
fi;
The problem is that it compares the number from the first digit on, i.e., 9 is bigger than 10, but 1 is greater than 09.
How can I convert the numbers into a type to do a true comparison?
In Bash, you should do your check in an arithmetic context:
if (( a > b )); then
...
fi
For POSIX shells that don't support (()), you can use -lt and -gt.
if [ "$a" -gt "$b" ]; then
...
fi
You can get a full list of comparison operators with help test or man test.
Like this:
#!/bin/bash
a=2462620
b=2462620
if [ "$a" -eq "$b" ]; then
echo "They're equal";
fi
Integers can be compared with these operators:
-eq # Equal
-ne # Not equal
-lt # Less than
-le # Less than or equal
-gt # Greater than
-ge # Greater than or equal
See this cheatsheet.
There is also one nice thing some people might not know about:
echo $(( a < b ? a : b ))
This code will print the smallest number out of a and b
In Bash I prefer doing this as it addresses itself more as a conditional operation unlike using (( )) which is more of arithmetic.
[[ n -gt m ]]
Unless I do complex stuff like
(( (n + 1) > m ))
But everyone just has their own preferences. Sad thing is that some people impose their unofficial standards.
You can also do this:
[[ 'n + 1' -gt m ]]
Which allows you to add something else which you could do with [[ ]] besides arithmetic stuff.
The bracket stuff (e.g., [[ $a -gt $b ]] or (( $a > $b )) ) isn't enough if you want to use float numbers as well; it would report a syntax error. If you want to compare float numbers or float number to integer, you can use (( $(bc <<< "...") )).
For example,
a=2.00
b=1
if (( $(bc <<<"$a > $b") )); then
echo "a is greater than b"
else
echo "a is not greater than b"
fi
You can include more than one comparison in the if statement. For example,
a=2.
b=1
c=1.0000
if (( $(bc <<<"$b == $c && $b < $a") )); then
echo "b is equal to c but less than a"
else
echo "b is either not equal to c and/or not less than a"
fi
That's helpful if you want to check if a numeric variable (integer or not) is within a numeric range.
One-line solution.
a=2
b=1
[[ ${a} -gt ${b} ]] && echo "true" || echo "false"
gt reference: https://www.gnu.org/software/bash/manual/html_node/Bash-Conditional-Expressions.html
&& reference: https://www.gnu.org/software/bash/manual/html_node/Shell-Arithmetic.html
[[...]] construct reference: https://www.gnu.org/software/bash/manual/bash.html#index-_005b_005b
${} reference: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02 (2.6.2)
The format for parameter expansion is as follows:
${expression}
where expression consists of all characters until the matching '}'.
Any '}' escaped by a or within a quoted string, and
characters in embedded arithmetic expansions, command substitutions,
and variable expansions, shall not be examined in determining the
matching '}'.
The simplest form for parameter expansion is:
${parameter}
This code can also compare floats. It is using AWK (it is not pure Bash). However, this shouldn't be a problem, as AWK is a standard POSIX command that is most likely shipped by default with your operating system.
$ awk 'BEGIN {return_code=(-1.2345 == -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 >= -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 < -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
1
$ awk 'BEGIN {return_code=(-1.2345 < 2) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 > 2) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
To make it shorter for use, use this function:
compare_nums()
{
# Function to compare two numbers (float or integers) by using AWK.
# The function will not print anything, but it will return 0 (if the comparison is true) or 1
# (if the comparison is false) exit codes, so it can be used directly in shell one liners.
#############
### Usage ###
### Note that you have to enclose the comparison operator in quotes.
#############
# compare_nums 1 ">" 2 # returns false
# compare_nums 1.23 "<=" 2 # returns true
# compare_nums -1.238 "<=" -2 # returns false
#############################################
num1=$1
op=$2
num2=$3
E_BADARGS=65
# Make sure that the provided numbers are actually numbers.
if ! [[ $num1 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num1 is not a number"; return $E_BADARGS; fi
if ! [[ $num2 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num2 is not a number"; return $E_BADARGS; fi
# If you want to print the exit code as well (instead of only returning it), uncomment
# the awk line below and comment the uncommented one which is two lines below.
#awk 'BEGIN {print return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}'
awk 'BEGIN {return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}'
return_code=$?
return $return_code
}
$ compare_nums -1.2345 ">=" -1.2345 && echo true || echo false
true
$ compare_nums -1.2345 ">=" 23 && echo true || echo false
false
If you have floats, you can write a function and then use that. For example,
#!/bin/bash
function float_gt() {
perl -e "{if($1>$2){print 1} else {print 0}}"
}
x=3.14
y=5.20
if [ $(float_gt $x $y) == 1 ] ; then
echo "do stuff with x"
else
echo "do stuff with y"
fi
I solved this by using a small function to convert version strings to plain integer values that can be compared:
function versionToInt() {
local IFS=.
parts=($1)
let val=1000000*parts[0]+1000*parts[1]+parts[2]
echo $val
}
This makes two important assumptions:
The input is a "normal SemVer string"
Each part is between 0-999
For example
versionToInt 12.34.56 # --> 12034056
versionToInt 1.2.3 # --> 1002003
Example testing whether npm command meets the minimum requirement...
NPM_ACTUAL=$(versionToInt $(npm --version)) # Capture npm version
NPM_REQUIRED=$(versionToInt 4.3.0) # Desired version
if [ $NPM_ACTUAL \< $NPM_REQUIRED ]; then
echo "Please update to npm#latest"
exit 1
fi
Just adding to all the above answers:
If you have more than one expression in single if statement, you can do something like this:
if (( $a % 2 == 0 )) && (( $b % 2 != 0));
then
echo "What you want to do"
fi
Hope this helps!

Why this command not found when i'm comparing strings in an array?

My sample code is here
#!/bin/bash
file="output2.txt"
numbers="$(cut -d',' -f2 output2.txt)"
lines="$(cut -f2 output2.txt)"
hours="$(cut -d',' -f1 output2.txt)"
array_numbers=( $numbers )
lines_array=( $lines )
hours_array=( $hours )
difference=$1
let range=$1-1000
for (( i = 0 ; i < ${#array_numbers[#]} ; i++ ))
do
let num=$(( 10#${array_numbers[$i+1]} - 10#${array_numbers[$i]} ))
if [ $num -gt $1 ]
then
echo ${lines_array[$i+1]} "and" ${lines_array[$i]} "has a difference more than $1"
elif [ $num -ge 0 ] && [ $num -lt $range ]
then
echo ${lines_array[$i+1]} "and" ${lines_array[$i]} "has a difference more than $1"
elif [ $num -le $1 ]
then
if [${hours_array[$i+1]} != ${hours_array[$i]}]
then
echo ${lines_array[$i+1]} "and" ${lines_array[$i]} "has a difference more than one second"
fi
fi
done
I'm working with the same output2.txt again:
12:43:40,317
12:43:40,318
12:43:40,332
12:43:40,333
12:43:40,334
12:43:40,335
12:43:40,336
12:43:40,337
12:43:40,338
12:43:40,339
12:43:40,353
12:43:40,354
12:43:40,356
12:43:40,358
12:43:40,360
12:43:40,361
12:43:40,362
12:43:40,363
12:43:40,364
12:43:40,365
12:43:40,382
12:43:40,384
12:43:40,385
12:43:40,387
12:43:40,388
12:43:40,389
12:43:40,390
12:43:40,391
12:43:40,404
12:43:40,405
12:43:40,406
12:43:40,407
12:43:40,408
12:43:40,409
12:43:40,410
12:43:40,412
12:43:40,413
12:43:40,414
12:43:40,415
12:43:40,428
12:43:40,429
12:43:40,431
12:43:40,432
12:43:40,433
12:43:40,434
12:43:40,435
12:43:40,436
12:43:40,437
12:43:40,438
12:43:40,440
12:43:40,443
12:43:40,458
12:43:40,459
12:43:40,460
12:43:40,461
12:43:40,462
12:43:40,463
12:43:40,464
12:43:40,465
12:43:40,466
12:43:40,479
12:43:40,480
12:43:40,481
12:43:40,482
12:43:40,483
12:43:40,484
12:43:40,485
12:43:40,486
12:43:40,487
12:43:40,501
12:43:40,503
12:43:40,504
12:43:40,505
12:43:40,506
12:43:40,509
12:43:40,510
12:43:40,511
12:43:40,512
12:43:40,513
12:43:40,514
12:43:40,515
12:43:40,517
12:44:40,518
What I want to do is take the difference as parameter and if there is a value difference more than 100 miliseconds than I'm wanna print output. The parts
for (( i = 0 ; i < ${#array_numbers[#]} ; i++ ))
do
let num=$(( 10#${array_numbers[$i+1]} - 10#${array_numbers[$i]} ))
if [ $num -gt $1 ]
then
echo ${lines_array[$i+1]} "and" ${lines_array[$i]} "has a difference more than $1"
elif [ $num -ge 0 ] && [ $num -lt $range ]
then
echo ${lines_array[$i+1]} "and" ${lines_array[$i]} "has a difference more than $1"
are actually working well , but i realized that if input has such a columns in order like the last part
12:43:40,517
12:44:40,518
it won't print anything so i put the last elif statement to my code but even it prints hours_array good, it doesn't work with while i'm comparing them. The output is always :
script.sh: line 22: [12:43:00: command not found
Why doesn't it accept this compare or is the problem is about my bash version ?
Thank you in advance for your help.
Add space before and after [. It is an 'alias' to the test buitin command.
You should also add double quote " around your variable. Because if they are empty, bash won't recognize them as a empty word.
And I generally use double brackets [[ for test condition which is more safer and has more features.
Example:
if [[ "${hours_array[$i+1]}" != "${hours_array[$i]}" ]]
You need a space here (the [ is a command)
if [ ${hours_array[$i+1]} != ${hours_array[$i]} ]
Missing space after [. [ is a command, so it needs to be separated from its arguments.
if [ ${hours_array[$i+1]} != ${hours_array[$i]} ]
I find few things that can be changed in this code.
Add space after [ and before ].
Add double quotes so that in case if variable is empty, script does not throw error.
if [ "${hours_array[$i+1]}" != "${hours_array[$i]}" ]
Also when you reach the last line, $i + 1 will fail. Hence, following would be better.
for (( i = 0 ; i < ${#array_numbers[#]} - 1 ; i++ ))

When/how to use "==" or "-eq" operator in test?

In the following code I want to compare the command line arguments with the parameters but I am not sure what is the current syntax to compare the arguments with parameters..i.e "==" or "-eq".
#!/bin/bash
argLength=$#
#echo "arg = $1"
if [ argLength==0 ]; then
#Running for the very first
#Get the connected device ids and save it in an array
N=0
CONNECTED_DEVICES=$(adb devices | grep -o '\b[A-Za-z0-9]\{8,\}\b'|sed -n '2,$p')
NO_OF_DEVICES=$(echo "$CONNECTED_DEVICES" | wc -l)
for CONNECTED_DEVICE in $CONNECTED_DEVICES ; do
DEVICE_IDS[$N]="$CONNECTED_DEVICE"
echo "DEVICE_IDS[$N]= $CONNECTED_DEVICE"
let "N= $N + 1"
done
for SEND_DEVICE_ID in ${DEVICE_IDS[#]} ; do
callCloneBuildInstall $SEND_DEVICE_ID
done
elif [ "$1" -eq -b ]; then
if [ $5 -eq pass ]; then
DEVICE_ID=$3
./MonkeyTests.sh -d $DEVICE_ID
else
sleep 1h
callCloneBuildInstall $SEND_DEVICE_ID
fi
elif [ "$1" -eq -m ]; then
echo "Check for CloneBuildInstall"
if [ "$5" -eq pass ]; then
DEVICE_ID=$3
callCloneBuildInstall $SEND_DEVICE_ID
else
echo "call CloneBuildInstall"
# Zip log file and save it with deviceId
callCloneBuildInstall $SEND_DEVICE_ID
fi
fi
function callCloneBuildInstall {
./CloneBuildInstall.sh -d $SEND_DEVICE_ID
}
From help test:
[...]
STRING1 = STRING2
True if the strings are equal.
[...]
arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne,
-lt, -le, -gt, or -ge.
But in any case, each part of the condition is a separate argument to [.
if [ "$arg" -eq 0 ]; then
if [ "$arg" = 0 ]; then
Why not use something like
if [ "$#" -ne 0 ]; then # number of args should not be zero
echo "USAGE: "
fi
When/how to use “==” or “-eq” operator in test?
To put it simply use == when doing lexical comparisons a.k.a string comparisons but use -eq when having numerical comparisons.
Other forms of -eq (equal) are -ne (not equal), -gt (greater than), -ge (greater than or equal), -lt (lesser than), and -le (lesser than or equal).
Some may also suggest preferring (( )).
Examples:
[[ $string == "something else" ]]
[[ $string != "something else" ]] # (negated)
[[ $num -eq 1 ]]
[[ $num -ge 2 ]]
(( $num == 1 ))
(( $num >= 1 ))
And always use [[ ]] over [ ] when you're in Bash since the former skips unnecessary expansions not related to conditional expressions like word splitting and pathname expansion.

Bash -eq and ==, what's the diff?

Why this works:
Output=$( tail --lines=1 $fileDiProva )
##[INFO]Output = "OK"
if [[ $Output == $OK ]]; then
echo "OK"
else
echo "No Match"
fi
and this not?
Output=$( tail --lines=1 $fileDiProva )
##[INFO]Output = "OK"
if [[ $Output -eq $OK ]]; then
echo "OK"
else
echo "No Match"
fi
What's the difference?? between == and -eq?
Thanks!
-eq is an arithmetic test.
You are comparing strings.
From help test:
Other operators:
arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne,
-lt, -le, -gt, or -ge.
When you use [[ and use -eq as the operator, the shell attempts to evaluate the LHS and RHS. The following example would explain it:
$ foo=something
+ foo=something
$ bar=other
+ bar=other
$ [[ $foo -eq $bar ]] && echo y
+ [[ something -eq other ]]
+ echo y
y
$ something=42
+ something=42
$ [[ $foo -eq $bar ]] && echo y
+ [[ something -eq other ]]
$ other=42
+ other=42
$ [[ $foo -eq $bar ]] && echo y
+ [[ something -eq other ]]
+ echo y
y
Have a look at this explanation of if.
The first one == is in the section of string compare operators and it can only compare two strings.
The second one -eq is in the last section ARG1 OP ARG2(last one) and its documentation says "ARG1" and "ARG2" are integers.
-eq, -lt, -gt is only used for arithmetic value comparison(integers).
== is used for string comparison.

How to compare a variable with a variable minus a constant in a linux shell script?

I want to compare a variable with another variable minus a constant in a linux shell script.
In cpp this would look like this:
int index = x;
int max_num = y;
if (index < max_num - 1) {
// do whatever
} else {
// do something else
}
In the shell i tried the following:
index=0
max_num=2
if [ $index -lt ($max_num - 1) ]; then
sleep 20
else
echo "NO SLEEP REQUIRED"
fi
I also tried:
if [ $index -lt ($max_num-1) ]; then
...
if [ $index -lt $max_num - 1 ]; then
...
if [ $index -lt $max_num-1 ]; then
...
but non of these versions works.
How do you write such a condition correctly?
Regards
The various examples that you tried do not work because no arithmetic operation actually happens in any of the variants that you tried.
You could say:
if [[ $index -lt $((max_num-1)) ]]; then
echo yes
fi
$(( expression )) denotes Arithmetic Expression.
[[ expression ]] is a Conditional Construct.
Portably (plain sh), you could say
if [ "$index" -lt "$((max_num-1))" ]; then
echo yes
fi
Short version
[ "$index" -lt "$((max_num-1))" ] && echo yes;
[ is the test program, but requires the closing ] when called as [. Note the required quoting around variables. The quoting is not needed when using the redundant and inconsistent bash extensions cruft ([[ ... ]]).
In bash, a more readable arithmetic command is available:
index=0
max_num=2
if (( index < max_num - 1 )); then
sleep 20
else
echo "NO SLEEP REQUIRED"
fi
The strictly POSIX-compliant equivalent is
index=0
max_num=2
if [ "$index" -lt $((max_num - 1)) ]; then
sleep 20
else
echo "NO SLEEP REQUIRED"
fi

Resources