16

This may be answered already but I am going to ask it anyways. I have two versions of a script (comp.sh)-

#!/bin/sh
export tDay=$(date '+%Y%m%d')
newfile="filename_$tDay"
filename="filename_20120821100002.csv"
echo $newfile $filename
if [ $filename = *$newfile* ]
then
  echo "Matched"
else
  echo "Not Matched!"
fi

Output:
$ ./comp.sh
filename_20120821 filename_20120821100002.csv
Not Matched!

And

#!/bin/sh
export tDay=$(date '+%Y%m%d')
newfile="filename_$tDay"
filename="filename_20120821100002.csv"
echo $newfile $filename
if [[ $filename = *$newfile* ]]
then
  echo "Matched"
else
  echo "Not Matched!"
fi

$ comp.sh
filename_20120821 filename_20120821100002.csv
Matched

Could someone explain me Why the difference?

Also - under what circumstances should [ ] be used vs. [[ ]] and vice versa?

Anjan Biswas
  • 7,746
  • 5
  • 47
  • 77
  • 5
    This question is similar and has a good answer that points to great documentation: http://stackoverflow.com/questions/669452/is-preferable-over-in-bash-scripts – malvim Aug 21 '12 at 22:03

3 Answers3

20

[[ is a bash built-in, and cannot be used in a #!/bin/sh script. You'll want to read the Conditional Commands section of the bash manual to learn the capabilities of [[. The major benefits that spring to mind:

  • == and != perform pattern matching, so the right-hand side can be a glob pattern
  • =~ performs regular expression matching. Captured groups are stored in the BASH_REMATCH array.
  • boolean operators && and ||
  • parenthèses for grouping of expressions.
  • no word splitting, so it's not strictly necessary to quote your variables.

The major drawback: your script is now bash-specific.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
10

test's string equality operator doesn't do globs.

$ [ abc = *bc ] ; echo $?
1
$ [[ abc = *bc ]] ; echo $?
0
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 2
    I can see that. But why, can you elaborate why `[ ]` vs. `[[ ]]` makes the difference? – Anjan Biswas Aug 21 '12 at 22:03
  • 1
    No, I can't. There are a great many things most people don't know, simply because the decision-making process that brought those things about was opaque. – Ignacio Vazquez-Abrams Aug 21 '12 at 22:05
  • Ok, so `[ ]` apparently was(is) a program `test`? – Anjan Biswas Aug 21 '12 at 22:08
  • @Annjawn: It is available as both a separate program and as a bash builtin. – Ignacio Vazquez-Abrams Aug 21 '12 at 22:09
  • Confusing! so is that the reason one should select `[[ ]]` over `[ ]`? – Anjan Biswas Aug 21 '12 at 22:10
  • 1
    @pst: From `help [[`: "When the `==' and `!=' operators are used, the string to the right of the operator is used as a pattern and pattern matching is performed." where "pattern" means "glob". – Ignacio Vazquez-Abrams Aug 21 '12 at 22:10
  • @IgnacioVazquez-Abrams Ahh, the double-`==` threw me off. That makes sense now, since `==` and `=` are (implicitly) stated to be aliases in the operator section .. –  Aug 21 '12 at 22:11
  • @Annjawn: No. One should use `[` only if `[[` is not available or does not follow the desired behavior. – Ignacio Vazquez-Abrams Aug 21 '12 at 22:11
  • @Annjawn- On most systems, [`[` is an alias for the command `test`](http://tldp.org/LDP/abs/html/testconstructs.html), and `]` is typically aliased to nothing (it's only there because matching braces are easier to read). `[[` is a keyword, not a command. – bta Aug 21 '12 at 22:28
4

Also - under what circumstances should [ ] be used vs. [[ ]] and vice versa?

It depends. If you care about portability and want your shell scripts to run on a variety of shells, then you should never use [[. If you want the features provided by [[ on some shells, you should use [[ when you want those features. Personally, I never use [[ because portability is important to me.

William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • 1
    But lets say I don't want to use `[[ ]]` but also want to do substring comparison as in my code above, how'd I do that? – Anjan Biswas Aug 21 '12 at 22:45
  • 1
    Are you looking for `if echo "$filename" | grep -qF "$newfile"; then ...` ? – William Pursell Aug 21 '12 at 23:07
  • something like that but without `grep`. So if i understand this, this is the only way to make my code compatible on multiple shell types and avoid using `[[`. – Anjan Biswas Aug 22 '12 at 00:23
  • its not the only way, you can also do `case "$filename" in *$newfile* ) ... ` – William Pursell Aug 22 '12 at 00:32
  • so do you suggest using these alternatives other than using `[[ $var = *var2* ]]` syntax? – Anjan Biswas Aug 22 '12 at 00:40
  • @Annjawn: For me, portability is extremely important and I usually run my scripts under shells that do not support the `[[` syntax. Under those circumstances, you must use one of these (or some other) alternative. If you do not mind restricting your script to run under a smaller set of shells, then it probably makes sense to use `[[`. – William Pursell Aug 22 '12 at 20:48