3793
Comment: lexicographically comparison IS possible with classic test, you just need to quote the operators
|
3550
converted to 1.6 markup
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
[[Anchor(faq31)]] | <<Anchor(faq31)>> |
Line 20: | Line 20: |
if [[ -e $file ]] | if [[ ! -e $file ]] |
Line 31: | Line 31: |
To cut a long story short: {{{[}}} implements the old, portable syntax of the command. Although all modern shells have built-in implementations, there usually still is an external executable of that name, e.g. {{{/bin/[}}}. {{{[[}}} is a new improved version of it, which is a keyword, not a program. This has beneficial effects on the ease of use, see below. {{{[[}}} is understood by KornShell, ["BASH"] (e.g. 2.03), KornShell93, and the ["POSIX"] shell, but not by the older BourneShell. | To cut a long story short: {{{[}}} implements the old, portable syntax of the command. Although all modern shells have built-in implementations, there usually still is an external executable of that name, e.g. {{{/bin/[}}}. {{{[[}}} is a new improved version of it, which is a keyword, not a program. This has beneficial effects on the ease of use, as shown below. {{{[[}}} is understood by KornShell and [[BASH]] (e.g. 2.03), but not by the older POSIX or BourneShell. |
Line 36: | Line 36: |
||<|4>string comparison||>||\>||-|| | ||<rowspan=4>string comparison||>||\>||-|| |
Line 38: | Line 38: |
||== (or =)||=||-|| | ||= (or ==)||=||-|| |
Line 40: | Line 40: |
||<|2>expression grouping||&&||-a||{{{[[ -n $var && -f $var ]] && echo "$var is a file"}}}|| | ||<rowspan=2>expression grouping||&&||-a||{{{[[ -n $var && -f $var ]] && echo "$var is a file"}}}|| |
Line 42: | Line 42: |
||Pattern matching||== (or =)||(not available)||{{{[[ $name = a* ]] || echo "name does not start with an 'a': $name"}}}|| ||In-process regular expression matching||=~||(not available)||{{{[[ $(date) =~ ^Fri\ ...\ 13 ]] && echo "It's Friday the 13th!"}}}|| |
||Pattern matching||= (or ==)||(not available)||{{{[[ $name = a* ]] || echo "name does not start with an 'a': $name"}}}|| ||RegularExpression matching||=~||(not available)||{{{[[ $(date) =~ ^Fri\ ...\ 13 ]] && echo "It's Friday the 13th!"}}}|| |
Line 54: | Line 54: |
* No field splitting will be done for {{{[[}}} (and therefore many arguments need not be quoted) | * No WordSplitting or [[glob]] expansion will be done for {{{[[}}} (and therefore many arguments need not be quoted): |
Line 67: | Line 67: |
* Parentheses in {{{[[}}} do not need to be escaped: {{{ [[ -f $file1 && ( -d $dir1 || -d $dir2) ]] [ -f "$file1" -a \( -d "$dir1" -o -d "$dir2" \) ]}}} |
|
Line 68: | Line 72: |
* No file name generation will be done for {{{[[}}}. Therefore the following line tries to match the contents of the variable {{{$path}}} with the pattern {{{/*}}} | As a rule of thumb, {{{[[}}} is used for strings and files. If you want to compare numbers, use an ArithmeticExpression, e.g. |
Line 70: | Line 74: |
{{{ [[ $path = /* ]] && echo "\$path starts with a forward slash /: $path"}}} |
{{{ # Bash i=0 while ((i<10)); do ... }}} |
Line 73: | Line 80: |
The next command most likely will result in an error, because {{{/*}}} is subject to file name generation: | When should the new test command {{{[[}}} be used, and when the old one {{{[}}}? If portability to the BourneShell is a concern, the old syntax should be used. If on the other hand the script requires [[BASH]] or KornShell, the new syntax is much more flexible. |
Line 75: | Line 82: |
{{{ [ $path = /* ] && echo "this does not work"}}} (If you need to do that using Bourne shells, use {{{case}}} instead.) * As a rule of thumb, {{{[[}}} is used for strings and files. If you want to compare numbers, use an ArithmeticExpression, e.g. {{{ i=0 while ((i<10)); do ...}}} When should the new test command {{{[[}}} be used, and when the old one {{{[}}}? If portability to the BourneShell is a concern, the old syntax should be used. If on the other hand the script requires ["BASH"] or KornShell, the new syntax is much more flexible. |
See the [[BashGuide/Practices/BashTests|Bash Tests]] chapter in the BashGuide. |
What is the difference between the old and new test commands ([ and [[)?
[ ("test" command) and [[ ("new test" command) are both used to evaluate expressions. Some examples:
if [ -z "$variable" ] then echo "variable is empty!" fi if [ ! -f "$filename" ] then echo "not a valid, existing file name: $filename" fi
and
if [[ ! -e $file ]] then echo "directory entry does not exist: $file" fi if [[ $file0 -nt $file1 ]] then echo "file $file0 is newer than $file1" fi
To cut a long story short: [ implements the old, portable syntax of the command. Although all modern shells have built-in implementations, there usually still is an external executable of that name, e.g. /bin/[. [[ is a new improved version of it, which is a keyword, not a program. This has beneficial effects on the ease of use, as shown below. [[ is understood by KornShell and BASH (e.g. 2.03), but not by the older POSIX or BourneShell.
Although [ and [[ have much in common, and share many expression operators like "-f", "-s", "-n", "-z", there are some notable differences. Here is a comparison list:
Feature |
new test [[ |
old test [ |
Example |
string comparison |
> |
\> |
- |
< |
\< |
- |
|
= (or ==) |
= |
- |
|
!= |
!= |
- |
|
expression grouping |
&& |
-a |
[[ -n $var && -f $var ]] && echo "$var is a file" |
|| |
-o |
- |
|
Pattern matching |
= (or ==) |
(not available) |
[[ $name = a* ]] || echo "name does not start with an 'a': $name" |
RegularExpression matching |
=~ |
(not available) |
[[ $(date) =~ ^Fri\ ...\ 13 ]] && echo "It's Friday the 13th!" |
Special primitives that [[ is defined to have, but [ may be lacking (depending on the implementation):
Description |
Primitive |
Example |
entry (file or directory) exists |
-e |
[[ -e $config ]] && echo "config file exists: $config" |
file is newer/older than other file |
-nt / -ot |
[[ $file0 -nt $file1 ]] && echo "$file0 is newer than $file1" |
two files are the same |
-ef |
[[ $input -ef $output ]] && { echo "will not overwrite input file: $input"; exit 1; } |
negation |
! |
- |
But there are more subtle differences.
No WordSplitting or glob expansion will be done for [[ (and therefore many arguments need not be quoted):
file="file name" [[ -f $file ]] && echo "$file is a file"
will work even though $file is not quoted and contains whitespace. With [ the variable needs to be quoted:
file="file name" [ -f "$file" ] && echo "$file is a file"
This makes [[ easier to use and less error-prone.
Parentheses in [[ do not need to be escaped:
[[ -f $file1 && ( -d $dir1 || -d $dir2) ]] [ -f "$file1" -a \( -d "$dir1" -o -d "$dir2" \) ]
As a rule of thumb, [[ is used for strings and files. If you want to compare numbers, use an ArithmeticExpression, e.g.
# Bash i=0 while ((i<10)); do ...
When should the new test command [[ be used, and when the old one [? If portability to the BourneShell is a concern, the old syntax should be used. If on the other hand the script requires BASH or KornShell, the new syntax is much more flexible.
See the Bash Tests chapter in the BashGuide.