I would like to recreate something like this
if ( arg1 || arg2 || arg 3) {}
And I did got so far, but I get the following error:
line 11: [.: command not found
if [ $char == $';' -o $char == $'\\' -o $char == $'\'' ]
then ...
I tried different ways, but none seemed to work. Some of the ones I tried.
For Bash, you can use the [[ ]] form rather than [ ], which allows && and || internally:
if [[ foo || bar || baz ]] ; then
...
fi
Otherwise, you can use the usual Boolean logic operators externally:
[ foo ] || [ bar ] || [ baz ]
...or use operators specific to the test command (though modern versions of the POSIX specification describe this XSI extension as deprecated -- see the APPLICATION USAGE section):
[ foo -o bar -o baz ]
...which is a differently written form of the following, which is similarly deprecated:
test foo -o bar -o baz
Bash's [[ ]] and (( )) are more powerful and flexible than [ ].
[[ ]] is for strings and (( )) is for integer logic and arithmetic
&& and || operators can be used inside [[ ]] and (( )), and () can be used for grouping
No need to quote variable expansions inside [[ ]] or (( )) - Bash doesn't do word splitting or globbing in these contexts
Inside (( )), there is no need for a $ behind variable names to expand them
[[ ]] and (( )) can span multiple lines, without the need for a line continuation with \
Using these, we can write clean, readable, and more reliable code.
Examples
Compound statements with integers
a=1 b=2 c=3
((a == 2 || (b == 2 && c == 3))) && echo yes # yields yes
Compound statements with strings
x=apple y=mango z=pear
[[ $x == orange || ($y == mango && $z == pear) ]] && echo yes # yields yes
[ equivalents for the above statements, using {}
[ "$a" -eq 2 ] || { [ "$b" -eq 2 ] && [ "$c" -eq 3 ]; }
[ "$x" == orange ] || { [ $y == mango ] && [ "$z" == pear ]; }
Related
Is double square brackets [[ ]] preferable over single square brackets [ ] in Bash?
How to use double or single brackets, parentheses, curly braces
Comparing integers: arithmetic expression or conditional expression on Unix & Linux
Test for non-zero length string in Bash: [ -n “$var” ] or [ “$var” ]
Conditional Expressions - Bash Manual
Charles' answer is correct in that it shows you how to do logical operations on commands within (and without, for that matter) an if statement, but it looks more like you want to use case here:
case $char in
\;|\\|\') echo found;;
*) echo 'not found';;
esac
In bash, to group conditionals using the [ ] construct you must surround each group using parenthesis. Each opening/closing parenthesis must be escaped and preceded/succeeded with a white space. See below:
if [ \( "$char" = "$;" \) -o \( "$char" = "$\\" \) -o \( "$char" = "$\" \) ]
As such, it's definitely best to follow everyone elses advice and use bash's newer [[ ]] construct. Lastly, as I understand it == is a relational operator intended to be used with-in arithmetic expressions. i.e. -
$((3==4))
Cameron Newham and Bill Rosenblatt, "Learning the bash Shell" Jan.1998
Related
Is there a difference between the two code snippets below
if [[ $a == "1" ]];then
echo $a
and
if [ $a == "1" ];then
echo $a
Also, is there a difference when I use -eq in place of == in the above snippet?
As for your main question: it is a duplicate of: Is [[ ]] preferable over [ ] in bash scripts?
You can also find a (hopefully) comprehensive discussion of the differences between [ ... ] and [[ ... ]] in this answer of mine.
In short: [[ ... ]] is parsed more like you'd expect in a regular programming language, and it implements many useful extensions, but it is not POSIX-compliant.
As for "is there a difference when I use -eq in place of ==?":
= and ==, its Bash alternative, perform string comparison.
Additionally, inside [[ ... ]] only, if the RHS of = or == is unquoted, it is interpreted as a glob-style pattern to match the LHS against; contrast [[ 'a' == '*' ]] && echo match with [[ 'a' == * ]] && echo match
Note that if you use [ ... ] (rather than [[ ... ]]) for POSIX compliance (portable use with /bin/sh), you should only use =, not ==; while Bash accepts == inside [ ... ] too, other shells don't.
-eq performs integer comparison
Other string/numeric operator pairs exist (e.g., -lt for numeric less-than vs. < for alphabetical (string) less-than, based on textual sort order).
Bash Conditional Expressions lists all operators you can use inside [ ... ] and [[ ... ]] (and also with test, which is effectively an alias of [ ... ]).
Additionally, inside [[ ... ]], regular expression-matching operator =~ is available - see Bash Conditional Constructs
In bash, numeric comparison is handled differently than string comparison
For numbers,
$var1 -eq $var2 // =
$var1 -gt $var2 // >
$var1 -ge $var2 // >=
$var1 -lt $var2 // <
$var1 -le $var2 // <=
$var1 -ne $var2 // !=
For strings
$str1 = $str2 // they are equal
str1 != str2 // not equal
str // Returns True if str is not null.
-n str // Returns True if the length of str is greater than zero.
-z str // Returns True if the length of str is equal to zero.
Note that == is the same as =
Also note that the == operates differently in a double bracket comparison (this is where your [ condition ] vs [[ condition ]] question comes in) when doing pattern matching. These comparisons/operators all all explained at http://www.tldp.org/LDP/abs/html/comparison-ops.html
Given your trying to check that a variable is not empty and not some other value as in the following code:
if [ ! -z "$foo" ] && [[ ${foo} != "bar" ]]; then
what is the best practice for accomplishing this. I've seen bash conditionals written several ways including the following...
if [[ ! -z "$foo" && ${foo} != "bar" ]]; then
I understand there is a difference when using the single brackets and the double, I'm more concerned with when to put the && or || inside the brackets or out.
Put &&/|| inside brackets for [[ ]]. Outside is also accepted.
Put &&/|| outside brackets for [ ]. Inside is NOT allowed.
This is due to the fact that && binds normal commands together based on return value, e.g.
wget file && echo "Success"
[, despite its funny name, is a regular command and obeys the same rules as e.g. wget or echo.
[ foo || bar ] is two commands, [ foo and bar ], neither of which are valid.
[[ .. ]] on the other hand is not a normal command but special shell syntax. [[ foo || bar ]] is a single command, and interpretted accordingly.
To complete the previous answers :
if [[ ! -z $foo && $foo != "bar" ]]; then ...
# [[ will execute the two conditions with "and" operator in a single instruction
Is equivalent of :
if [[ ! -z $foo -a $foo != "bar" ]]; then ...
# [[ will execute the two conditions with "and" operator in a single instruction
But not equivalent of :
if [[ ! -z $foo ]] && [[ $foo != "bar" ]]; then ...
# second [[ will be executed if the first success ($? = 0)
-a (and) and -o (or) will work with test and [.
See man test to get more details ;)
Otherwise, no need to protect your variables by doubles quotes with [[ and no need to use delimiters (${}) in this case.
Here is a reminder about the necessity (or not) to protect your variables with double quotes..
In Bash, is there a simple way to test if one string is lexicographically less than or equal to another?
I know you can do:
if [[ "a" < "b" ]]
for testing strict inequality, or
if [[ 1 -le 1 ]]
for numbers. But -le doesn't seem to work with strings, and using <= gives a syntax error.
Just negate the greater than test:
if [[ ! "a" > "b" ]]
You need to use || with an additional condition instead of <=:
[[ "$a" < "$b" || "$a" == "$b" ]]
You can flip the comparison and sign around and test negatively:
$ a="abc"
$ b="abc"
$ if ! [[ "$b" > "$a" ]] ; then echo "a <= b" ; fi
a <= b
If you want collating sequence of "A" then "a" then "B"... use:
shopt -s nocaseglob
If you can use the Bash syntax, see the answers from #anubhava and #gordon-davisson. With POSIX syntax you have two options (note the necessary backslashes!):
using the -o operator (OR):
[ "$a" \< "$b" -o "$a" = "$b" ] && echo "'$a' LTE '$b'" || echo "'$a' GT '$b'"
or using negation:
[ ! "$a" \> "$b" ] && echo "'$a' LTE '$b'" || echo "'$a' GT '$b'"
I prefer the first variant, because imho it's more readable.
expr POSIX method
I believe [ a < b ] is a Bash extension. The best POSIX method I could find was this as documented at http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html:
expr abc \< acb >/dev/null || echo fail
! expr abc \< aac >/dev/null || echo fail
or with slightly worse golfing and a subshell:
[ "$(expr abc \< acb)" = 1 ] || echo fail
[ "$(expr abc \< aac)" = 0 ] || echo fail
But because expr \< is completely insane and:
automatically determines is something is an integer or not to choose numerical vs lexicographical comparison, e.g. expr 2 \< 10 is 1
has undefined behaviour for magic keywords length, substr, index and match
you generally want to add a trash x to force your variable to be a non-reserved string as in:
x1=aac
x2=abc
x3=acb
expr x"$x2" \< x"$x3" >/dev/null || echo fail
! expr x"$x2" \< x"$x1" >/dev/null || echo fail
and so for less than or equal I'd just:
expr x"$x1" \< x"$x2" >/dev/null || [ "$x1" = "$x2" ] || echo fail
sort POSIX workaround
Just for fun, use expr instead.
Not infinitely robust to strings with newlines, but when is it ever when dealing with shell scripts?
string_lte() (
s="$(printf "${1}\n${2}")"
if [ "$(printf "$s" | sort)" = "$s" ]; then
exit 0
else
exit 1
fi
)
string_lte abc adc || echo fail
string_lte adc adc || echo fail
string_lte afc adc && echo fail
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
$
What is the difference between [ test ] and [[ test ]] in bash?
When is one more appropriate than the other and what does the ; at the end do?
if [[ -z $DIRECTORY ]];
then
DIRECTORY=html
fi
if [ ! -d "$DIRECTORY" ]; then
echo installation directory "'${DIRECTORY}'" does not exist
exit 1
fi
[[ is a bash keyword similar to (but more powerful than) the [ command. See http://mywiki.wooledge.org/BashFAQ/031 and http://mywiki.wooledge.org/BashGuide/TestsAndConditionals Unless you're writing for POSIX sh, we recommend [[.
We usually use single square brackets when we:
Check something with files and want to use patterns (e.g. asterisk): if [ -L $file ]; then
Check artithmetic expressions: if [ $a -lt $b ]; then
Check something with strings and want to use " " and treat special characters as normal (e.g. asterisk): if [ -z "$string" ]; then
We usually double square brackets when we:
Want to use pattern with string (e.g. asterisk): if [[ "$string1" == *[sS]tring* ]]; then
Block patterns in file names (e.g. asterisk) e.g. we search file named *.sh: if [[ -a *.sh ]]; then
Want to use operators && and ||: if [[ $a == 3 || $b == 4]]; then
Don't want to put strings in " "
[ is for shell, [[ is for bash.
For example :
Try [ $A -eq 1 ]: if $A is not set, it raise an error.
[[ $A -eq 1 ]] will works.