Why does echo > 1 2 3 create a file called 1 with contents 2 3?

A proper resource you can refer to is the POSIX shell grammar, which defines a simple command as:

simple_command   : cmd_prefix cmd_word cmd_suffix
                 | cmd_prefix cmd_word
                 | cmd_prefix
                 | cmd_name cmd_suffix
                 | cmd_name
                 ;

The most relevant part, here, is the definition of command_suffix:

cmd_suffix       :            io_redirect
                 | cmd_suffix io_redirect
                 |            WORD
                 | cmd_suffix WORD
                 ;

which is recursive, allowing for redirections and command arguments to appear in any order.

Also, POSIX defines the syntax for redirection as

[n]redir-op word

While no space is allowed between the optional number n and the redirection operator (>, in your case), an arbitrary amount of space is allowed between the redirection operator and the following word. After expansion, word (1, in your case) is used as the name of the file the stream is directed to (or from).

Thus, it is equally legal to write

$ echo  > 1  foo  bar
# ^^^^  ^^^^^^^^^^^^^
#  \     \
#   \     cmd_suffix
#    \  ^^^  ^^^  ^^^
#     \  \    \    \
#      \  \    \    WORD
#       \  \    WORD
#        \  io_redirect
#         cmd_name

or

$ echo foo >1 bar

or even

$ echo > 1 foo > 1 bar > 1

(where, of course, repeating > 1 serves no purpose).

For the sake of completeness: the cmd_prefix in the definition of simple_command is itself recursively defined:

cmd_prefix       :            io_redirect
                 | cmd_prefix io_redirect
                 |            ASSIGNMENT_WORD
                 | cmd_prefix ASSIGNMENT_WORD
                 ;

meaning that redirections and variable assignments can appear in any order before a command.

You may then have, for instance,

$ LC_ALL=C <infile sort >outfile 2>errfile
# ^^^^^^^^^^^^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^
#       \            \           \
#   cmd_prefix    cmd_word   cmd_suffix

or

$ 2>errfile >outfile <infile LC_ALL=C sort

or

$ LC_ALL=C sort <infile 2>errfile >outfile

Which are all equally legal, but it has to be kept in mind that redirections and variable assignments, as well as expansions, are performed from left to right and their order may be relevant (e.g. if infile does not exist, outfile is not truncated in cat <infile >outfile while it is in cat >outfile <infile).


Words are split on whitespace. A quoted string is treated as a single word regardless of whitespace. A variable's value is a candidate for word splitting when it's used (which is why it's usually best to double-quote the variable name, "$var").

The redirection operator uses the following single word.

The full detail can be seen in the man page for your shell (eg man bash under "Expansions")

The order of expansions is: brace expansion, tilde expansion, parameter, variable and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, and pathname expansion.