At first, when your terminal and shell starts, both STDOUT and STDERR points to the terminal output. Your command echo "potato" >&2
is asking to redirect STDOUT to what STDERR points to. Thus this command has no effect at all.
Here's some references:
- File descriptor 0 (FD0) is named STDIN
- File descriptor 1 (FD1) is named STDOUT
- File descriptor 2 (FD2) is named STDERR
- File descriptor 3 (FD3) and more has no special names.
- Any unix program initially has all those 3 file descriptors opened by default.
- STDIN by default points to the input device (of the terminal opening the program), normally finally to keyboard
- STDOUT, STDERR by default point to the console (terminal) output
> somefile
or 1> somefile
redirects file descriptor 1 to the file named 'somefile', i.e. pointing STDOUT to 'somefile'
n> somefile
redirects file descriptor n to the file named 'somefile', where n
= 1, 2, 3, ... n
by default is 1, when n
is omitted.
n>&m
redirects file descriptor n to file descriptor m.
- When we say file descriptor n is redirected to file descriptor m, we actually mean that file descriptor n is forced to point to what file descriptor m pointing to, for example, the terminal output device/console.
n>&-
closes file descriptor n, where n = 1, 2, 3, ...
- The order of the redirections are important. They are applied from left to right. See example below:
# FD1 (file descriptor1, i.e. STDOUT) is redirected (pointing) to the file named 'file.log`
# Then FD2 is pointing to what FD1 points to, i.e. 'file.log'. Thus the following command
# redirection both "potato" and the error output of 'ls xx' to file.log:
$ (echo "potato"; ls xx) > file.log 2>&1
$ cat file.log
potato
ls: cannot access xx: No such file or directory
# FD2 is redirected (pointing) to what FD1 points to, i.e. the terminal output. (This has no effect, since FD2 was pointed to the terminal anyway.
# FD1 is then redirected to file.log
# Thus the following command only redirects "potato" to file.log, and left the error message
# displayed on the terminal.
$ (echo "potato"; ls xx) 2>&1 > file.log
ls: cannot access xx: No such file or directory
$ cat file.log
potato
- Ordering between redirection and pipe (Pipe first, and then the redirections). So, this command:
command1 > /dev/null | comamnd2
first create pipe between command1, and command2, i.e. link STDOUT of command 1 to STDIN of command2. Then, STDOUT of command1 is redirected to /dev/null. This essentially cancels the pipe (disengages the pipe). Thus command2 will sees end of the STDIN input, i.e. STDIN of command2 is closed.
So, it explains why the following command exchanges STDIN and STDOUT:
$ (echo xx; ls xx) 3>&1 1>&2 2>&3 3>&- | wc -l
ls: cannot access xx: No such file or directory
1
- Step1: pipe is created, FD1 (left side) points to pipe-out. FD0 of
wc
points to pipe-in
- Step2: FD3 is created, duplicating FD1, i.e. pointing to pipe-out
- Step3: FD1 is now changed to what FD2 pointing to, i.e. the terminal output.
- Step4: FD2 is now changed to what FD3 pointing to, i.e. the pipe-out.
- Step5: FD3 is closed.
The net effects are:
FD1 now points to the terminal output.
FD2 now points to the pipe-out, piping outputs to wc
command.
Hope this helps.