Why is a blank required between "[[" and "-e xxx" in ksh?

For instance, the following command does not work:

if [[-e xyz]]; then echo File exists;fi

ksh gives the following error

[[-e: command not found

Is that because "[[-" is ambiguous?


Solution 1:

The simplest explanation would be, because in the manual it appears as [[ expression ]] so there has to be space between [[ and expression and closing ]]. But of course we can attempt to look at it more in depth. Shells have somewhat complex grammar and rely heavily on the concept of "word splitting". In particular, ksh(1) manual states:

The shell begins parsing its input by breaking it into words. Words, which are sequences of characters, are delimited by unquoted white-space characters (space, tab and newline) or meta-characters (<, >, |, ;, &, ( and )). Aside from delimiting words, spaces and tabs are ignored, while newlines usually delimit commands.

So as stated in the manual, sequence of characters [[-e is considered a shell word. ksh would look for such command in the list of built-ins and special operators ( like for or while ), then look for an external command - and voilà - none found, hence there's an error message about command not found.

In this case [[ are not special meta-characters either. If they were, the -e part in [[-e would be considered a shell word, not an argument to [[ itself. However, [[ is described as compound command, and the manual says the following:

Compound commands are created using the following reserved words - these words are only recognized if they are unquoted and if they are used as the first word of a command (i.e., they can't be preceded by parameter assignments or redirections):

So in order for [[ to be recognized as a compound command, it has to be a first word of a command or command list, which by previous definition of a "word" implies it has to be separated by spaces,tabs,or newlines from other words/arguments.


You've asked in the comments: "Why can't the parser stop as soon as it finds "[[" pattern, and treats that as the start of a conditional command?" Short answer is probably because 1) influence of [ syntax since it was originally an external command, and for POSIX standard compliance exists as external command even today, and 2) because the shell parser is built so. Shell parser can recognize other non-space delimited special characters: echo $((2+2)) and (echo foobar) work perfectly fine. Maybe in future when ksh development resumes or there appears to be a fork or clone (like mksh or pdksh) someone will implement space-less syntax in [[-e.

See also:

  • In terms of POSIX grammar, [[ is called a "reserved word" (see section 2.9 of Shell Command Language). By POSIX definition, reserved word has to be delimited by spaces. By contrast ( is an operator, and the standard states in section 2.9 "the representations include spacing between tokens in some places where <blank>s would not be necessary (when one of the tokens is an operator)". See related discussion: POSIX Shell Grammar: Why Brace Group Needs First Space But Subshell Doesn't?

Solution 2:

What is important to note is that [ is a command, and the shell keyword [[ in bash and ksh is based on [.

[[ is used in a similar way to [. Sometimes you can even replace [ ] with [[ ]] with no change in behavior. With [, like any command, a space is mandatory between the command and its arguments. The same applies to [[.

We used to have only /usr/bin/[. Now most shells have [ built in for efficiency—but the syntax is the same. In shells that provide [[, it functions as a more versatile alternative to [.

Here is the description for [ in bash:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

So [ is equivalent to test (aside from expecting a ] argument at the very end). help test will give even more detail on it. You can compare this to help [[.

There is also a man page for the external command [ (man \[).

In the case of

if [[-e
  • Neither [ nor [[ is a command, there. The full word [[-e is.
  • The if [[-e makes it a test for true/false. So does a command [[-e exist?

Is that because "[[-" is ambiguous?

Yes. Or no. [[-e is what it is: nothing that the shell understand so it assumes it is its own command. ;-)

Solution 3:

The space is a deliminator and is required. As you can see from shellcheck:

$ shellcheck gmail-browse-msgs-algorithm.sh

In gmail-browse-msgs-algorithm.sh line 847:
        [[$Today != "${DaysArr[ i + DAY_DELETED_ON_NDX ]}" ]] && continue
          ^-- SC1035: You need a space after the [[ and before the ]].

(Both ksh and bash support [[ and don't work without the space. Shellcheck gives exactly that output with similar ksh and bash scripts containing that buggy line.)

Why deliminators are needed has to do with tokens and lexicons.