cat somefile >file1 >file2
Without redirections cat somefile
would inherit stdin from the shell. Usually stdin of a shell is not closed, it already points to some file. A terminal is also a file. When you run cat somefile
in an interactive shell whose stdout is your terminal, the output of cat
goes to the terminal. In general the stdout of the shell may be something else, irrelevant, cat
will inherit it anyway.
What cat somefile >file1
does is it makes the shell:
- try to open
file1
for writing (it creates the file if it's not already there) and to truncate it to zero size (if possible); this happens before cat
is started;
- set up
file1
as stdout for the future cat
, so the cat
will use file1
instead of what it would use without the redirection (you don't expect cat somefile >file1
to also write to your terminal, right?).
The redirection redirects. :) The current direction is "to the terminal", the new direction is "to file1
". The new direction is taken instead of the current direction and the new direction becomes current.
The shell parses redirections from left to right. In case of cat somefile >file1 >file2
the shell first handles >file1
in the way described above. Then it handles >file2
in the same way: it opens and truncates the file, and sets up "to file2
" for the future cat
instead of the current direction which at this point is already "to file1
". This way "to file2
" replaces "to file1
" and becomes current, exactly like "to file1
" replaced "to the terminal" a moment ago.
You can add more >fileN
redirections and each one will be handled in the described way. Assuming each one succeeds, the last one will win. In your case >file2
wins.
After all the redirections are handled, cat
is started. Your cat
sees file2
as its stdout and it's not even aware file1
was involved in any way. The truncation of file1
is a "side effect", it's only because >file1
has been parsed by the shell, it has nothing to do with cat
.
You can use this mechanism to create/truncate one or more files: : >file1 >file2 …
will try to truncate the files. :
is a no-op, it doesn't even use its stdout. Creating and truncating happens when the shell handles the redirections before starting :
.
This is how output redirections work in sh
and compatible shells. The MULTIOS option in zsh
(see this other answer) is different: the first redirection replaces the old direction (e.g. the terminal) while consecutive redirections add directions. How it happens under the hood, I won't elaborate. I just want to point out that the behavior you observed emerges from the fact in sh
(or in a compatible shell) each >fileN
is handled independently, one after another; but in zsh
there's a mode where it treats multiple output redirections more like a unit.
>
redirects the output to a file, period. Usetee(1)
if you want to duplicate output to a file and send it on.cat somefile >file1 3>file2 4>file3
. This means you start the program with two extra files opened as fileno 3 and fileno 4. Of course,cat
will ignore those, but it can be useful if you want to have different log files, for example, or you're somehow splitting the input in 3 parts, etc.