Shell Builtin – Why `[` is a Shell Builtin and `[[` is a Shell Keyword

shellshell-builtin

As far as I know, [[ is an enhanced version of [, but I am confused when I see [[ as a keyword and [ being shown as a builtin.

[root@server ~]# type [
[ is a shell builtin
[root@server ~]# type [[
[[ is a shell keyword

TLDP says

A builtin may be a synonym to a system command of the same name, but
Bash reimplements it internally. For example, the Bash echo command is
not the same as /bin/echo, although their behavior is almost
identical.

and

A keyword is a reserved word, token or operator. Keywords have a
special meaning to the shell, and indeed are the building blocks of
the shell's syntax. As examples, for, while, do, and ! are keywords.
Similar to a builtin, a keyword is hard-coded into Bash, but unlike a
builtin, a keyword is not in itself a command, but a subunit of a
command construct. [2]

Shouldn't that make both [ and [[ a keyword? Is there anything that I am missing here?
Also, this link re-affirms that both [ and [[ should belong to the same kind.

Best Answer

The difference between [ and [[ is quite fundamental.

  • [ is a command. Its arguments are processed just the way any other commands arguments are processed. For example, consider:

    [ -z $name ]
    

    The shell will expand $name and perform both word splitting and filename generation on the result, just as it would for any other command.

    As an example, the following will fail:

    $ name="here and there"
    $ [ -n $name ] && echo not empty
    bash: [: too many arguments
    

    To have this work correctly, quotes are necessary:

    $ [ -n "$name" ] && echo not empty
    not empty
    
  • [[ is a shell keyword and its arguments are processed according to special rules. For example, consider:

    [[ -z $name ]]
    

    The shell will expand $name but, unlike any other command, it will perform neither word splitting nor filename generation on the result. For example, the following will succeed despite the spaces embedded in name:

    $ name="here and there"
    $ [[ -n $name ]] && echo not empty
    not empty
    

Summary

[ is a command and is subject to the same rules as all other commands that the shell executes.

Because [[ is a keyword, not a command, however, the shell treats it specially and it operates under very different rules.