Redirection of echo's standard output to standard error, still produces output in terminal?

547 views Asked by At

I'm a bit confused as to why when I'm redirecting the standard output of an echo command to the standard error, why I still get the argument printed in the terminal?

Here's is the line I ran

echo "potato" >&2

Could someone explain this to me? How does this command output anything, if the output was redirected somewhere else?

Thank you :)

5

There are 5 answers

0
rottweilers_anonymous On

Well, by default, your terminal shows both STDOUT and STDERR.

So, what you are seeing is STDERR.

If you want to hide STDERR: echo "potato" 2>/dev/null >&2

/dev/null is a black-hole, where you can redirect stuff you have no wish to see :)

0
Nicola Ferraro On

Because standard error is also displayed in terminal by default. So you redirect your standard output to standard error which in turn gets redirected to the console. The result does not change.

1
z0lo On

I think what you want is:

bash-3.2$ echo "potato" &>2
bash-3.2$ 

From the man page for bash:

Redirecting Standard Output and Standard Error
       Bash  allows  both  the  standard  output (file descriptor 1) and 
       the standard error output (file descriptor 2) to be redirected to the file 
       whose name is the expansion of word with this construct.

       There are two formats for redirecting standard output and standard error:

              &>word
       and
              >&word

       Of the two forms, the first is preferred.  This is semantically equivalent to

              >word 2>&1
0
Robin Hsu On

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.

0
Reinstate Monica Please On

The output is simply going where it's told to go

By default file descriptor 1 and 2 point to the same location (note >&2 is equivalent to 1>&2)

> $ ls -l /proc/$$/fd/
total 0
lrwx------ 1 foo foo 64 Dec 23 18:42 0 -> /dev/pts/3
lrwx------ 1 foo foo 64 Dec 23 18:42 1 -> /dev/pts/3
lrwx------ 1 foo foo 64 Dec 23 18:42 2 -> /dev/pts/3

And now let's say we redirect one of those file descriptors to point elsewhere

> exec 1>foo
> ls -l /proc/$$/fd/
> exec 1>&2
> cat foo
total 0
lrwx------ 1 foo foo 64 Dec 23 18:42 0 -> /dev/pts/3
lrwx------ 1 foo foo 64 Dec 23 18:42 1 -> /home/foo/foo
lrwx------ 1 foo foo 64 Dec 23 18:42 2 -> /dev/pts/3    

Note the output of ls -l /proc/$$/fd/ went to file foo in our working directory rather than being printed to stdout.