Differences between revisions 4 and 9 (spanning 5 versions)
Revision 4 as of 2007-08-30 02:53:52
Size: 3819
Editor: GreyCat
Comment: one typo, a few minor changes
Revision 9 as of 2008-05-16 18:48:53
Size: 3546
Editor: GreyCat
Comment: clean up
Deletions are marked like this. Additions are marked like this.
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||>||(not available)||-||
||<||(not available)||-||
||== (or =)||=||-||
||<rowspan=4>string comparison||>||\>||-||
||<||\<||-||
||= (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"}}}

 The next command most likely will result in an error, because {{{/*}}} is subject to file name generation:

 {{{
 [ $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 ...}}}
{{{
# Bash
i=0
while ((i<10)); do ...
}}}
Line 87: Line 81:

See the [:BashGuide/Practices/BashTests:Bash Tests] chapter in the BashGuide.

Anchor(faq31)

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 [:BashGuide/Practices/BashTests:Bash Tests] chapter in the BashGuide.

BashFAQ/031 (last edited 2022-05-09 13:49:40 by 27)