Command Redirection Executed Before a Command

redirect

In many Linux books is written that 'redirection is executed before a command'.
Let's consider:

$ cat bar
hello
$ ecno "foo" > bar
bash: ecno: command not found
$cat bar
  1. If bar would not exist, redirection would created it.
  2. If bar exists and a command did not work, it erases the content of bar.

But it does not copy any output into bar (since there is no output), so it should be said 'redirection is executed PARTIALLY before a command', since the one part of '>' did not work here, namely copying the output of the command into a file bar. Is it right?

Best Answer

From bash manual section 3.6.2:

...if it does exist it is truncated to zero size.

Redirection with > file always truncates file before executing command, which means contents are erased regardless of the output; the actual writing to file will take place if there is an actual output (most notably observed with the lack of write() call in strace -f -e open,dup2,write bash -c 'true > out2.txt') and if no further error occurs.

Therefore, everything works properly, as per specifications, and the wording is correct. In fact this behavior is specified by POSIX Shell Command Language specifications and is supported by all POSIX-compliant shells, including ksh and dash (aka Ubuntu's /bin/sh, see the related question).


On the system level, redirection is performed by dup family of system calls, which is why redirection is formally called duplicating file descriptors and is most prominent when we perform the >/dev/null 2&>1 type of redirection.

It can be observed with strace command in the following example of successful and failed command. Note how the files are opened with O_CREAT|O_TRUNC flag.

$ strace -f -e open,dup2,write bash -c 'nocommand > out.txt;echo "foobar" > out2.txt'
...
strace: Process 9633 attached
[pid  9633] open("out.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
[pid  9633] dup2(3, 1)                  = 1
...
...
[pid  9633] write(2, "bash: nocommand: command not fou"..., 35bash: nocommand: command not found
) = 35
[pid  9633] +++ exited with 127 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=9633, si_uid=1000, si_status=127, si_utime=0, si_stime=0} ---
open("out2.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
dup2(3, 1)                              = 1
write(1, "foobar\n", 7)                 = 7
dup2(10, 1)                             = 1
+++ exited with 0 +++
Related Question