69

Assuming file2 already exists, the command

> file1 < file2 cat

appears to be copying the content of file2 to file1.

But I cannot understand this structure.

I understand that "Nothing" is being directed to file1 (creating or erasing its content). Then the content of file2 is being directed to file1.

Why is cat after file2? How does it know to cat file2 if the operands are not in the correct order?

2
  • 11
    What "correct order"? Can you provide a link to some resource / learning material where a "correct order" is described? Commented Apr 5, 2017 at 17:35
  • 8
    When asking questions like this, you should specify the shell; different ones have different edge cases, especially around redirection. Commented Apr 5, 2017 at 23:26

1 Answer 1

133

Before the shell executes the cat command on the command line, it handles any redirections. Redirections include redirecting input or output using < (stdin, read-only), <> (stdin, read+write), > (stdout, write-only) and >> (stdout, write-only, appending output), but also <<word (here-documents) and <<<'...' (in some shells, "here-strings"). These redirection operators should occur unquoted to have the effect of redirecting input or output, and you can prepend a number to specify which file descriptor to redirect instead of the default (stdin (0) or stdout (1) depending on the operator).

There are two redirections in the command that you show:

  1. >file1 This will make the command's standard output go to file1.
  2. <file2 This will make the command's standard input come from file2.

The fact that these redirections are placed in a wonky location on the command line doesn't matter.

$ cat <file2 >file1

is the same as

$ <file2 cat >file1

which is the same as

$ <file2 >file1 cat

etc.¹

Note that the cat utility in all of these instances is executed without any command line arguments. The redirections are not operands to the cat command, they are instructions to the shell to set up redirections into and out of the command (connecting its standard input and output to files). The shell sets up the redirections before invoking the command.

The difference between cat file and cat <file (or, if you will, <file cat) is that in the first case, the cat utility itself is opening the file, which is given as an operand on the command line, for reading, while in the second case, the shell will open the file and connect cat's input stream to it². In the second case, cat will notice it wasn't given a file operand and will automatically switch to reading from its standard input. This is a feature of cat, and of some other utilities, not something that all utilities do.

cat will also read from its standard input if it's given the operand -. Again, this is special only to cat and to some other utilities (i.e. nothing that the shell does). To use cat on a file in the current directory whose name is -, add a path to the filename, such as ./-.

¹ The order of the redirections is still important under some circumstances; With cat <file2 >file1, for example, file1 will not be truncated if file2 is inaccessible (the redirections are parsed from left to right). The relative placement of the word cat is however still arbitrary and won't influence this.

² See also the question cat gives different error when opening non-existing file.


The fact that the shell sets up the redirections before even executing the command on the command line is why things like these fail and you end up with an empty output file:

$ sort file >file

Here, the shell will truncate (empty) the file file before executing sort file and connecting sort's standard output to the file. The sort utility will then open file and sort its contents (which is nothing). The result (nothing) is passed through the standard output stream to file.

The remedy in this particular case (for sorting a file "in-place") is

$ sort -o file file

or

$ sort file >file.sorted && mv file.sorted file

or, to ensure that the original file is not deleted (to preserve certain file meta-data),

$ cp file file.unsorted && sort file.unsorted >file && rm -f file.unsorted

which is more or less what sort does when using the -o file to specify the output file name.


Just to back up the statement that the redirections may precede the actual name of the utility on the command line (my emphasis):

A "simple command" is a sequence of optional variable assignments and redirections, in any sequence, optionally followed by words and redirections, terminated by a control operator. [ref: POSIX Shell Command Language 2.9.1 Simple Commands]

And also about the redirection not being part of the operands of the utility:

The optional number, redirection operator, and word shall not appear in the arguments provided to the command to be executed (if any). [ref: POSIX Shell Command Language 2.7 Redirection]

12
  • 15
    @Steve The freedom to move redirections around can be used to make some commands clearer, like a pipeline with input redirection at the beginning <in foo | bar >out puts everything in logical order. I'm also fond if echo >&2 Something bad happened for error output from shell scripts. But >out <in cat is just obfuscation
    – user41515
    Commented Apr 5, 2017 at 14:42
  • 1
    @Wossname The most common issue with permissions and redirections is probably conjunction with sudo, e.g. sudo grep something file1 >file2 when in a directory only writable by root. The redirection fails even before the sudo command is invoked.
    – Kusalananda
    Commented Apr 5, 2017 at 14:47
  • 1
    @Wossname Whether the shell or the utility opens a file is less of a performance issue in general. Repeatedly appending to an output file is slow if it requires the shell (or the utility) to re-open the file for each additional bit of data. In those cases it's better to open the file for appending with >> once and then iterate over the utility. This is most often done by placing >>outfile after the done in the loop, rather than with the invocation of the utility. But, as I said, the difference between letting the utility or the shell open a file is, I assume, minimal.
    – Kusalananda
    Commented Apr 5, 2017 at 14:54
  • 1
    @Wossname BTW, sudo grep something file1 | sudo tee file2 >/dev/null will solve the permission issue I mentioned.
    – Kusalananda
    Commented Apr 5, 2017 at 14:59
  • 3
    Something to be aware of: when linking to POSIX with HTML fragments, it's better to specify the exact edition (like pubs.opengroup.org/onlinepubs/9699919799.2016edition) as fragments have been known to change between editions of a same revision of the spec (a lot of links in answers on this site including of mine are now wrong as the fragments point in the wrong place after the 2016 edition has been released). Commented Apr 7, 2017 at 8:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .