Differences between revisions 1 and 16 (spanning 15 versions)
Revision 1 as of 2007-05-02 23:19:34
Size: 3784
Editor: redondos
Comment:
Revision 16 as of 2011-10-24 19:02:58
Size: 4794
Editor: service
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
[[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:
<<Anchor(faq31)>>
== What is the difference between test, [ and [[ ? ==
{{{[}}} ("test" command) and {{{[[}}} ("new test" command) are used to evaluate expressions. Some examples:
Line 11: Line 11:
    if [ -f "$filename" ]     if [ ! -f "$filename" ]
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 benefical effects on the ease of use, see below. {{{[[}}} is understood by KornShell, ["BASH"] (e.g. 2.03), KornShell93, ["POSIX"] shell, but not by the older BourneShell. To cut a long story short: {{{test}}} implements the old, portable syntax of the command.  In almost all shells (the oldest Bourne shells are the exception), {{{[}}} is a synonym for {{{test}}} (but requires a final argument of {{{]}}}). Although all modern shells have built-in implementations of {{{[}}}, 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 38:
||<rowspan="4">string comparison||>||(not available)||-||
||<||(not available)||-||
||== (or =)||=||-||
||!=||!=||-||
||<rowspan="2">expression grouping||&&||-a||{{{[[ -n $var && -f $var ]] && echo "$var is a file"}}}||
||<rowspan=6>integer comparison||>(or -gt)||\>(or -gt)||{{{[[ 5 > 6 ]] || echo 5 is not bigger than 6}}}||
||<(or -lt)||\<(or -lt)||{{{[[ 8 < 9 ]] && echo 8 is less than 9}}}||
||-ge||-ge||{{{[[ 3 -ge 3 ]] && 3 is greater than or equal to 3}}}||
||-le||-le||{{{[[ 3 -le 8 ]] && 3 is less than or equal to 8}}}||
||= (or == or -eq)||=(or -eq)||{{{[[ 5 == 5 ]] && 5 equals 5}}}||
||!=||!=||{{{[[ 6 == 20 ]] && 6 is not equal to 20}}}||
||decimal Comparison||(available)||(not available)||{{{[[ 5.8 > 5.3 ]] && echo 5.8 is greater than 5.3}}}||
||<rowspan=2>expression grouping||&&||-a||{{{[[ -n $var && -f $var ]] && echo "$var is a file"}}}||
Line 42: Line 47:
||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!"}}}||

The effectiveness of these varies depending on what you're comparing. `-lt` will not work with a decimal while `<` will. `<` sometimes has trouble with integers which cross multiples of two boundaries (depending on your OS, bash version and environment variables)where -lt does not.
Line 54: Line 61:
 * No field splitting will be done for {{{[[}}} (and therefore many arguments need not to be quoted)  * No WordSplitting or [[glob]] expansion will be done for {{{[[}}} (and therefore many arguments need not be quoted):
Line 66: Line 73:
 This makes {{{[[}}} easier to use and less error prone.  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" \) ]}}}
Line 68: Line 79:
 * 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 of bash 4.1, string comparisons using `<` or `>` respect the current [[locale]] when done in `[[`, but '''not''' in `[` or `test`. In fact, `[` and `test` have ''never'' used locale collating order even though past man pages ''said'' they did. Bash versions prior to 4.1 do not use locale collating order for `[[` either.
Line 70: Line 81:
 {{{
 [[ $path = /* ]] && echo "\$path starts with a forward slash /: $path"}}}
As a rule of thumb, {{{[[}}} is used for strings and files. If you want to compare numbers, use an ArithmeticExpression, e.g.
Line 73: Line 83:
 The next command most likely will result in an error, because {{{/*}}} is subject to file name generation: {{{
# Bash
i=0
while ((i<10)); do ...
}}}
Line 75: Line 89:
 {{{
 [ $path = /* ] && echo "this does not work"}}}
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 78: Line 91:
 {{{[[}}} is strictly used for strings and files. If you want to compare numbers, use ArithmethicExpression ((''expression'')), e.g.

 {{{
 i=0
 while ((i<10))
 do
    echo $i
    ((i=$i+1))
 done}}}

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 could be preferable.
See the [[BashGuide/TestsAndConditionals|Tests and Conditionals]] chapter in the BashGuide.
----
CategoryShell

What is the difference between test, [ and [[ ?

[ ("test" command) and [[ ("new test" command) are 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: test implements the old, portable syntax of the command. In almost all shells (the oldest Bourne shells are the exception), [ is a synonym for test (but requires a final argument of ]). Although all modern shells have built-in implementations of [, 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

integer comparison

>(or -gt)

\>(or -gt)

[[ 5 > 6 ]] || echo 5 is not bigger than 6

<(or -lt)

\<(or -lt)

[[ 8 < 9 ]] && echo 8 is less than 9

-ge

-ge

[[ 3 -ge 3 ]] && 3 is greater than or equal to 3

-le

-le

[[ 3 -le 8 ]] && 3 is less than or equal to 8

= (or == or -eq)

=(or -eq)

[[ 5 == 5 ]] && 5 equals 5

!=

!=

[[ 6 == 20 ]] && 6 is not equal to 20

decimal Comparison

(available)

(not available)

[[ 5.8 > 5.3 ]] && echo 5.8 is greater than 5.3

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!"

The effectiveness of these varies depending on what you're comparing. -lt will not work with a decimal while < will. < sometimes has trouble with integers which cross multiples of two boundaries (depending on your OS, bash version and environment variables)where -lt does not.

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 of bash 4.1, string comparisons using < or > respect the current locale when done in [[, but not in [ or test. In fact, [ and test have never used locale collating order even though past man pages said they did. Bash versions prior to 4.1 do not use locale collating order for [[ either.

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 Tests and Conditionals chapter in the BashGuide.


CategoryShell

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