11
$ echo > 1 2 3; ls -latr 1; cat 1
-rw-rw-r-- 1 kahn kahn 4 Jun 23 14:05 1
2 3

Does this have to do with the evaluation of > redirection? Such that:

echo > 1 2 3

Could also be rewritten as and is technically:

echo 2 3 > 1

?

Are there any resources for better understanding the order of operations with IO redirection and how it is evaluated? I admit the example is not something that would probably be useful or even occur often, but I'd like to better understand what exactly is happening here.

4

2 Answers 2

21

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).

7
  • Wow, those grammar symbols have always scared me. I think you've nailed it, but for all it to make sense we have to associate an argument to echo as a WORD, such that the argument could come after the redirection. I.e., in OP's sample: cmd_word = echo; io_redirection = > WORD = 1, WORD = 2, WORD = 3. What do you think?
    – Quasímodo
    Jun 23, 2020 at 19:29
  • 1
    @Quasímodo I agree and attempted a... graphical clarification.
    – fra-san
    Jun 23, 2020 at 19:58
  • So following your example of echo > 1 foo > 1 bar > 1, does that mean the result of echo > 1 foo > 2 bar > 3 is well defined?
    – roaima
    Jun 23, 2020 at 21:07
  • 1
    @roaima Interesting question. I'd think it is: the way I read the POSIX spec, three redirections would be performed, from left to right, truncating files 1, 2 and 3; standard output would end up directed to 3, the other files would remain empty. It's also what dash and Bash do (and ksh, mksh, oksh, yash...). zsh does not, it duplicates the output to all the three files.
    – fra-san
    Jun 23, 2020 at 21:25
  • 1
    Note that cmd_prefix also includes io_redirect, so you can put the redirection first: > 1 echo 2 3
    – cjm
    Jun 24, 2020 at 4:58
4

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.

1
  • In the Bourne-like shell languages, whitespace (at least SPC, TAB, NL, more blank characters with some shells) are not the only token delimiters. <, >, |, ;, &, (, ) also are (or can be). See for example [[(a||b)&&(c)]]|{(true>/dev/null|cat&)} in bash which has many tokens but no whitespace. Jun 24, 2020 at 11:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.