I'm writing a VERY simple bash script with an "IF" statment. I've written many before, but for some reason it will not read the "${hitA1} == 0" argument. ${hitA1} is read from a file, and it is actually reading it, and it does actually equal 0. Instead it goes to the second argument of "${hitA1} != 0". Any ideas?
Code:
CorHitA1=0
MissA1=0
for i in 44 50 53 58 71
do
late=`expr $i + 1`
hitA1=`sed -n ${i}p _TMP_Acc1.txt`
hitlateA1=`sed -n ${late}p _TMP_Acc1.txt`
if [[ ${hitA1} == 0 ]]; then
echo "HEY!!!!!"
CorHitA1=`expr ${CorHitA1} + 1`
elif [[ ${hitA1} != 0 ]]; then
echo "Nope..."
echo ${hitA1}
fi
echo "CorHitA1 = " ${CorHitA1}
done
With bash, you should use (( for arithmetic tests:
if ((hitA1 == 0)); then ...
(With arithmetic evaluation, you don't need the $ nor do you need to quote the variable.)
Or you can use the -eq operator with [[:
if [[ $hitA1 -eq 0 ]]; then ...
If you don't do one of the above, and the line in the file you're extracting has whitespace in it, then [[ $hitA1 == 0 ]] will return false because == is string equality, and a string with whitespace is not the same as a string without.
Related
I currently have code as
if [[ "$FIRSTFLAG" == 1 ]] ; then
all_comp+=("FIRST")
fi
if [[ "$SECONDFLAG" == 1 ]] ; then
all_comp+=("SECOND")
fi
if [[ "$THIRDFLAG" == 1 ]] ; then
all_comp+=("THIRD")
fi
all_comp is just an array
So, im working on a solution to reduce the repetitive code
I know that we can use case here.
I wonder if there is a solution that can be done using array and for loop \
For example(I know its syntactically wrong)
names=("FIRST" "SECOND" "THIRD")
for i in $names[#]; do
if [[ ${i}FLAG == 1 ]]; then <- This line is the issue
all_comp+=("$i")
fi
done
So please tell me if there is a solution for such code example
You need to use indirect expansion by saving the constructed variable name, e.g. iflag=${i}FLAG, then you can use access the indirect expansion with ${!iflag}, e.g.
FIRSTFLAG=1
SECONDFLAG=0
THIRDFLAG=1
all_comp=()
names=("FIRST" "SECOND" "THIRD")
for i in ${names[#]}; do
iflag=${i}FLAG
if [[ ${!iflag} == 1 ]]; then
all_comp+=("$i")
fi
done
echo ${all_comp[#]} # Outputs: FIRST THIRD
Oh another answer, you can make use of the arithmetic expansion operator (( )) i.e.
FIRSTFLAG=1
SECONDFLAG=0
THIRDFLAG=1
all_comp=()
names=("FIRST" "SECOND" "THIRD")
for i in ${names[#]}; do
if (( ${i}FLAG == 1 )); then
all_comp+=("$i")
(( ${i}FLAG = 99 ))
fi
done
echo ${all_comp[#]} # FIRST THIRD
echo $FIRSTFLAG # 99
echo $SECONDFLAG # 0
echo $THIRDFLAG # 99
Reference:
https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html#Shell-Parameter-Expansion
We have some scripts that do things like
e=$?
if [[ $e == 123 ]]; then exit 1; fi
They're more complicated than that, it's just an example. My question is using double brackets acceptable to make numerical comparisons this way, is there any disadvantage? I would think it should be double parentheses if (( $e == 123 )) but I don't want to go changing a lot of scripts over nothing.
Thanks
There are a lot of key differences doing it, because == checks for exact string equality, but -eq evaluates both expressions arithmetically before checking for equality.
$ [[ " 1 " -eq 1 ]] && echo equal || echo not
equal
$ (( " 1 " == 1 )) && echo equal || echo not
equal
$ [[ " 1 " = 1 ]] && echo equal || echo not
not
Also, the empty string happens to be numerically equal to zero:
$ [[ "" -eq 0 ]] && echo equal || echo not
equal
$ [[ "" == 0 ]] && echo equal || echo not
not
And a whole other class of differences appears when you bring the comparison operators in - considering < vs -lt, for instance:
$ [[ 2 -lt 10 ]] && echo less || echo not
less
$ (( 2 < 10 )) && echo less || echo not
less
$ [[ 2 < 10 ]] && echo less || echo not
not
This is because the string 2 is alphabetically after the string 10 (since 1 comes before 2), but the number 2 is numerically less than the number 10.
Credits to the original cross site duplicate, with a few updates Is there any major difference when comparing a variable as a string or as an int?
The verdict is to use $((..)) for arithmetic comparisons strictly to avoid interpreting the operands as strings.
I'm trying to understand why an unset variable gets evaluated as 0.
in some scripts im writing the variable will be set only if needed and some time it does not.
so this kind of behave will result with incorrect output.
does it mean i must preset all my variables or at least add check that they are set ?
#!/bin/bash
#myvalue=0 #comment to simulate an unset variable.
if [[ $myvalue -eq 0 ]] ; then
echo "OK"
fi
result with OK:
bash -x test.sh
+ [[ '' -eq 0 ]]
+ echo OK
OK
The -eq operator inside [[ ... ]], since it only applies to integer values, triggers arithmetic evaluation of its operands. In an arithmetic expression, unset variables default to 0. A more obvious demonstration of arithmetic evaluation:
$ if [[ 3 -eq "1 + 2" ]]; then echo equal; fi
equal
Note that in your example, you don't even need to expand the parameter first; the arithmetic evaluation will do it for you:
$ if [[ myvalue -eq 0 ]]; then echo equal; fi
equal
$ myvalue=3
$ if [[ myvalue -eq 3 ]]; then echo equal; fi
equal
Also, this is specific to the bash [[ ... ]] command. With POSIX [, -eq does not trigger arithmetic evaluation.
$ if [ "$myvalue" -eq 0 ]; then echo equal; fi
bash: [: : integer expression expected
$ if [ myvalue -eq 0 ]; then echo equal; fi
bash: [: myvalue: integer expression expected
If you want the literal value to be the comparison use = instead of -eq.
if [[ $myvalue = 0 ]] ; then
echo "OK"
fi
The arithmetic binary operator (-eq) returns true if arg1 is equal to 0, which $myvalue is, whether set to 0 or not set at all... '' is null, which equals zero.
What is the difference between =, == and -eq in shell scripting?
Is there any difference between the following?
[ $a = $b ]
[ $a == $b ]
[ $a -eq $b ]
Is it simply that = and == are only used when the variables contain numbers?
= and == are for string comparisons
-eq is for numeric comparisons
-eq is in the same family as -lt, -le, -gt, -ge, and -ne
== is specific to bash (not present in sh (Bourne shell), ...). Using POSIX = is preferred for compatibility. In bash the two are equivalent, and in sh = is the only one that will work.
$ a=foo
$ [ "$a" = foo ]; echo "$?" # POSIX sh
0
$ [ "$a" == foo ]; echo "$?" # bash-specific
0
$ [ "$a" -eq foo ]; echo "$?" # wrong
-bash: [: foo: integer expression expected
2
(Note: make sure to quote the variable expansions. Do not leave out the double-quotes above.)
If you're writing a #!/bin/bash script then I recommend using [[ instead. The double square-brackets [[...]] form has more features, a more natural syntax, and fewer gotchas that will trip you up. For example, double quotes are no longer required around $a:
$ [[ $a == foo ]]; echo "$?" # bash-specific
0
See also:
What's the difference between [ and [[ in Bash?
It depends on the Test Construct around the operator. Your options are double parentheses, double brackets, single brackets, or test.
If you use ((…)), you are testing arithmetic equality with == as in C:
$ (( 1==1 )); echo $?
0
$ (( 1==2 )); echo $?
1
(Note: 0 means true in the Unix sense and a failed test results in a non-zero number.)
Using -eq inside of double parentheses is a syntax error.
If you are using […] (or single brackets) or [[…]] (or double brackets), or test you can use one of -eq, -ne, -lt, -le, -gt, or -ge as an arithmetic comparison.
$ [ 1 -eq 1 ]; echo $?
0
$ [ 1 -eq 2 ]; echo $?
1
$ test 1 -eq 1; echo $?
0
The == inside of single or double brackets (or the test command) is one of the string comparison operators:
$ [[ "abc" == "abc" ]]; echo $?
0
$ [[ "abc" == "ABC" ]]; echo $?
1
As a string operator, = is equivalent to ==. Also, note the whitespace around = or ==: it’s required.
While you can do [[ 1 == 1 ]] or [[ $(( 1+1 )) == 2 ]] it is testing the string equality — not the arithmetic equality.
So -eq produces the result probably expected that the integer value of 1+1 is equal to 2 even though the right-hand side is a string and has a trailing space:
$ [[ $(( 1+1 )) -eq "2 " ]]; echo $?
0
While a string comparison of the same picks up the trailing space and therefore the string comparison fails:
$ [[ $(( 1+1 )) == "2 " ]]; echo $?
1
And a mistaken string comparison can produce a completely wrong answer. 10 is lexicographically less than 2, so a string comparison returns true or 0. So many are bitten by this bug:
$ [[ 10 < 2 ]]; echo $?
0
The correct test for 10 being arithmetically less than 2 is this:
$ [[ 10 -lt 2 ]]; echo $?
1
In comments, there is a question about the technical reason why using the integer -eq on strings returns true for strings that are not the same:
$ [[ "yes" -eq "no" ]]; echo $?
0
The reason is that Bash is untyped. The -eq causes the strings to be interpreted as integers if possible including base conversion:
$ [[ "0x10" -eq 16 ]]; echo $?
0
$ [[ "010" -eq 8 ]]; echo $?
0
$ [[ "100" -eq 100 ]]; echo $?
0
And 0 if Bash thinks it is just a string:
$ [[ "yes" -eq 0 ]]; echo $?
0
$ [[ "yes" -eq 1 ]]; echo $?
1
So [[ "yes" -eq "no" ]] is equivalent to [[ 0 -eq 0 ]]
Last note: Many of the Bash specific extensions to the Test Constructs are not POSIX and therefore may fail in other shells. Other shells generally do not support [[...]] and ((...)) or ==.
== is a bash-specific alias for = and it performs a string (lexical) comparison instead of a numeric comparison. eq being a numeric comparison of course.
Finally, I usually prefer to use the form if [ "$a" == "$b" ]
Several answers show dangerous examples. The OP's example, [ $a == $b ], specifically used unquoted variable substitution (as of the October 2017 edit). For [...] that is safe for string equality.
But if you're going to enumerate alternatives like [[...]], you must inform also that the right-hand-side must be quoted. If not quoted, it is a pattern match! (From the Bash man page: "Any part of the pattern may be quoted to force it to be matched as a string.").
Here in Bash, the two statements yielding "yes" are pattern matching, other three are string equality:
$ rht="A*"
$ lft="AB"
$ [ $lft = $rht ] && echo yes
$ [ $lft == $rht ] && echo yes
$ [[ $lft = $rht ]] && echo yes
yes
$ [[ $lft == $rht ]] && echo yes
yes
$ [[ $lft == "$rht" ]] && echo yes
$
if [[ $line == *"option 1"* ]]
then
CURRENT_OPTION=1
fi
if [[ $line == *"option 2"* ]]
then
CURRENT_OPTION=2
fi
if [[ $line =~ "What i want" ]]
then
if [[ $CURRENT_OPTION -eq 1 ]]
then
MEM1=$(awk '/Used heap/ { gsub(/M/, " "); print $4 }')
elif [[ $CURRENT_OPTION -eq 2 ]]
then
MEM2=$(awk '/Used heap/ { gsub(/M/, " "); print $4 }')
fi
fi
Because CURRENT_OPTION is defined within an if, its value is not correct when checked in the third if. How do I pass it out so that it is?
Just declare CURRENT_OPTION at the top, something like:
declare -i CURRENT_OPTION=0
i to declare it as an int.
In all of your if statements you should enclose the variables in double quotes. If the variable is an empty string (or if the variable doesn't exist) then the if statement will not contain enough arguments and will throw an error.
Here is an example:
if [[ $var -eq 1 ]]
then
echo yes
else
echo no
fi
If var is uninitialised, bash will expand the statement to look like this:
if [[ -eq 1 ]]
then
echo yes
else
echo no
fi
There are not enough arguments to make the if statement valid here, and bash will throw an error:
bash: conditional binary operator expected
bash: syntax error near `1'
By wrapping the variable in quotes, this situation is avoided. This statement:
if [[ "$var" -eq 1 ]]
...
is expanded to:
if [[ "" -eq 1 ]]
...
and now the if statement has enough arguments (the first one being an empty string) to parse.