[[ " stop start status " =~ " $2 " && (($#<3)) ]] || { echo "Usage $0 file_name command"; exit 1;}
I frequently use the above solution to check the input range of my Bash script.
Now I realise that the extended arithmetic expression (())
looks like it is suppressed inside the double bracket [[]]
.
To illustrate the problem:
a=start; n=1; [[ " stop start status " =~ " $a " && (($n<3)) ]] && echo ok || echo bad
ok
a=start; n=5; [[ " stop start status " =~ " $a " && (($n<3)) ]] && echo ok || echo bad
bad
# But:
a=start; n=100; [[ " stop start status " =~ " $a " && (($n<3)) ]] && echo ok || echo bad
ok
The above result is false because n not less than 3 if they are treated as numbers. This is the correct solution:
a=start; n=100; [[ " stop start status " =~ " $a " ]] && (($n<3)) && echo ok || echo bad
bad
a=start; n=1; [[ " stop start status " =~ " $a " ]] && (($n<3)) && echo ok || echo bad
ok
Trying to use cd inside the shell script does not work because the shell script runs in the subshell and once the script is over it returns to the parent shell, which is why the current directory does not change.
Bash indenting is very sensitive to characters. For example a space behind “do” in while/for loops will throw it of. When you have nested loops this is very ugly, and makes it hard to follow the code.
The origin of != is the C family of programming languages, in which the exclamation point generally means "not". In bash, a ! at the start of a command will invert the exit status of the command, turning nonzero values to zero and zeroes to one.
flag is the iterator variable here. In bash the do followed by while statement specifies starting of block which contains satement to be executed by while . The ending of block is specified by done .
The GNU bash man page for [[..]]
explains that the operator runs a conditional expression and
Return a status of
0
or1
depending on the evaluation of the conditional expressionexpression
. Expressions are composed of the primaries described below in Bash Conditional Expressions.
But the arithmetic operator is not part of the supported conditional expressions (primaries) inside [[..]]
which means the expression is forced to run as a string comparison, i.e.
(( $n < 3))
is not run in arithmetic context but just as plain lexicographic (string) comparison as
[[ 100 < 3 ]]
which will always result true, because the ASCII values for 1
, 0
, 0
appear before 3
But inside [[..]]
arithmetic operations are supported if you use -lt
, -gt
arg1 OP arg2
OP is one of
-eq
,-ne
,-lt
,-le
,-gt
, or-ge
. These arithmetic binary operators return true ifarg1
is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal toarg2
, respectively.
So had you written your expression as
a=start; n=100; [[ " stop start status " =~ " $a " && $n -lt 3 ]] && echo ok || echo bad
bad
it would have worked as expected.
Or even if you had forced the arithmetic expression usage by prefixing $
before ((..))
and written it as below (note that bash does not have documented behavior for $((..))
inside [[..]]
). The likely expected behavior is the arithmetic expression is expanded before the [[..]]
is evaluated and the resultant output is evaluated in a string context as [[ 0 ]]
which means a non-empty string.
a=start; n=5; [[ " stop start status " =~ " $a " && $(( $n < 3 )) ]] && echo ok || echo bad
The result would still look bad, because the arithmetic expression inside [[..]]
decomposes into an unary string not empty comparison expression as
$(( 5 < 3 ))
0
[[ -n 0 ]]
The result of the arithmetic evaluation 0
(false) is taken as a non-zero entity by the test operator and asserts true on the right-side of &&
. The same would apply for the other case also e.g. say n=1
$(( 1 < 3 ))
1
[[ -n 1 ]]
So long story short, use the right operands for arithmetic operation inside [[..]]
.
((
is a "keyword" that introduces the arithmetic statement. Inside [[
, however, you can't use other statements. You can use parentheses to group expressions though, so that's what (( ... ))
is: a redundant "double group". The following are all equivalent, due to the precedences of <
and &&
:
[[ " stop start status " =~ " $2 " && (($#<3)) ]]
[[ " stop start status " =~ " $2 " && ($#<3) ]]
[[ " stop start status " =~ " $2 " && $#<3 ]]
If you want integer comparison, use -lt
instead of <
, but you also don't need to fit everything inside [[ ... ]]
. You can use a conditional statement and an arithmetic statement together in a command list.
{ [[ " stop start status " =~ " $2 " ]] && (($#<3)) ; } || { echo "Usage $0 file_name command"; exit 1;}
In this case, ... && ... || ...
will work the way you expect, though in general that is not the case. Prefer an if
statement instead.
if [[ " stop start status " =~ " $2 " ]] && (($#<3)); then
echo "Usage $0 file_name command"
exit 1
fi
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With