So,
Being new to GIT and thus extremely rusty in my bash commands and scripting I’ve been looking around for different syntax and scripting help. Now, I’ve found a lot of help and have been able to create the scripts and alias that will make my Git experience more pleasant.
However, I came across some nuances that seem to confuse me, specifically related to the “if” command.
if [ -z $1 ] ; #<- Zero length string if [[ -z $1 ]] ; #<- Also Zero Length String if [[ "$1" == -* ]] ; #<- Starts with - (hyphen) if [ -z $1 ] && [ -z $2 ] ; #<- both param 1 & 2 are zero length if [[ -z $1 ]] && [[ -z $2 ]] ; #<- Also both param 1 & 2 are zero length if [[ "$1" == -* ]] || [[ "$2" == -* ]] ; #<- Either param 1 or 2 starts with - if [ "$1" == -* ] || [ "$2" == -* ] ; #<- Syntax Failure, "bash: ]: too many arguments"
Why the discrepancy? How to know when the [[ (double) is required and when a [ (single) will do?
Thanks
Jaeden “Sifo Dyas” al’Raec Ruiner
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
First off, note that neither type of bracket is part of the syntax for
if. Instead:
[is another name for the shell built-intest;[[ ... ]]is a separate built-in, with different syntax and semantics.
Here are excerpts from the bash documentation:
[ / test
test: test [expr]
Evaluate conditional expression.
Exits with a status of 0 (true) or 1 (false) depending on
the evaluation of EXPR. Expressions may be unary or binary. Unary
expressions are often used to examine the status of a file. There
are string operators and numeric comparison operators as well.
The behavior of test depends on the number of arguments. Read the
bash manual page for the complete specification.
File operators:
-a FILE True if file exists.
(...)
[[ ... ]]
[[ ... ]]: [[ expression ]]
Execute conditional command.
Returns a status of 0 or 1 depending on the evaluation of the conditional
expression EXPRESSION. Expressions are composed of the same primaries used
by the `test' builtin, and may be combined using the following operators:
( EXPRESSION ) Returns the value of EXPRESSION
! EXPRESSION True if EXPRESSION is false; else false
EXPR1 && EXPR2 True if both EXPR1 and EXPR2 are true; else false
EXPR1 || EXPR2 True if either EXPR1 or EXPR2 is true; else false
When the `==' and `!=' operators are used, the string to the right of
the operator is used as a pattern and pattern matching is performed.
When the `=~' operator is used, the string to the right of the operator
is matched as a regular expression.
The && and || operators do not evaluate EXPR2 if EXPR1 is sufficient to
determine the expression's value.
Exit Status:
0 or 1 depending on value of EXPRESSION.
More simply said, [ requires one to take the normal care needed for
bash expressions, quote to avoid interpolation, etc. So a proper way
of testing for $foo being the empty string, or being unset, would be:
[ -z "$foo" ]
or
[[ -z $foo ]]
It’s important to quote in the first case, because setting foo="a b"
and then testing [ -z $foo ] would result in test -z receiving two
arguments, which is incorrect.
The language for [[ .. ]] is different, and properly knows about
variables, much in the way one would expect from a higher-level
language than bash. For this reason, it is much less error-prone than
classic [ / test.
Method 2
Please type man bash and read the documentation.
Things you will find there:
if list; then list; [ elif list; then list; ] ... [ else list; ] fi
The if list is executed. If its exit status is zero, the
then list is executed. Otherwise, each elif list is executed
in turn, and if its exit status is zero, the corresponding
then list is executed and the command completes. Otherwise,
the else list is executed, if present. The exit status is
the exit status of the last command executed, or zero if no
condition tested true.
This means after if there can be any command and bash only cares about the return value of that command. You used two different commands above: [ and [[.
test expr
[ expr ]
Return a status of 0 (true) or 1 (false) depending on the
evaluation of the conditional expression expr. Each operator
and operand must be a separate argument. Expressions are
composed of the primaries described above under CONDITIONAL
EXPRESSIONS. test does not accept any options, nor does it
accept and ignore an argument of -- as signifying the end of
options. [...]
This is the classic test available on many shells.
[[ expression ]]
Return a status of 0 or 1 depending on the evaluation of the
conditional expression expression. Expressions are composed
of the primaries described below under CONDITIONAL EXPRES‐
SIONS. Word splitting and pathname expansion are not per‐
formed on the words between the [[ and ]]; tilde expansion,
parameter and variable expansion, arithmetic expansion, com‐
mand substitution, process substitution, and quote removal
are performed. Conditional operators such as -f must be
unquoted to be recognized as primaries.
When used with [[, the < and > operators sort lexicographi‐
cally using the current locale.
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 Match‐
ing, as if the extglob shell option were enabled. The =
operator is equivalent to ==. If the shell option nocase‐
match is enabled, the match is performed without regard to
the case of alphabetic characters. The return value is 0 if
the string matches (==) or does not match (!=) the pattern,
and 1 otherwise. Any part of the pattern may be quoted to
force the quoted portion to be matched as a string.
This is an extension in bash with slightly different meaning. Especially == is defined differently. The first does literal comparison, while the second dose wildcard matching.
Method 3
When in doubt (and in bash), always use double-braces ([[ ]]), as they are a superset of the single-braces which are more prone to user error in their invocation.
[ is an internal command in bash, but in other environments can be a symlink to /bin/test; the corresponding ] is actually ignored and is there for aesthetics. if /bin/test -z "$VAR" and if [ -z "$VAR" ] are identical statements, at the end of the day (notwithstanding that [ may or may not invoke a subprocess in the name of /bin/test, depending on your environment).
[[ ]] is also an internalism for bash, so there’s no subshell, is a superset of [ ], and also allows for more friendly writing which just Does The Right Thing where [ ] would explode.
http://mywiki.wooledge.org/BashFAQ/031 goes into a lot more detail.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0