How to tee to stderr?
Asked Answered
L

4

70

I want to split stdout so that it is printed both to stdout and stderr. This sounds like a job for tee but the syntax is evading me -

./script.sh | tee stderr

Of course, how should stderr actually be referred to here?

Lancelle answered 10/12, 2012 at 16:26 Comment(6)
@JonathanLeffler There are systems with /dev/fd but without the symlinks stderr, stdout. I think that includes Solaris? A few outliers, like HP-UX, AIX, Irix don't have /dev/fd, of course, but all sane systems do.Duvalier
My bad, Solaris 8 and 9 do have /dev/stderr...Duvalier
Really? Solaris 10 has /dev/stderr. I've not encountered a system with /dev/fd that does not also have /dev/std{in,out,err}, but that doesn't mean they don't exist. Mac OS X 10.7.5 has both; Solaris has both; Linux has both. AIX 6 has neither; HP-UX 11.00 has neither.Stableman
Possible dupe with lots of potential answers: #692500Benetta
bash accepts /dev/stderr as a synonym for standard error whether or not the file exists in the file system; it's a special case.Harri
Regarding /dev/fd/2 and /dev/stderr; bash manual on redirections: Bash handles several filenames specially when they are used in redirections, ...Timmytimocracy
D
65
./script.sh | tee /dev/fd/2

Note that this is dependant on OS support, not any built-in power in tee, so isn't universal (but will work on MacOS, Linux, Solaris, FreeBSD, probably others).

Duvalier answered 10/12, 2012 at 16:32 Comment(8)
For a better explanation of what is taking place behind the scenes in the above command, read the bash hackers site on redirection: wiki.bash-hackers.org/syntax/redirectionBenetta
It's not really redirection, is it? It's just pipe syntax. The /dev/fd/2 argument is opaque to the shell; it only sets up the stdin fd up for the tee child, which is the process that interprets the argument (as a filename, to obtain the output fd with open()).Duvalier
The manpage for bash implies that it accepts /dev/fd/2 as a file name for file descriptor 2 even the file system doesn't have such a file.Harri
@Harri That's interesting. But, in this example the filename isn't ever interpreted by bash, so that isn't coming into play here.Duvalier
Oh, right. It would work with redirection, but not as an argument to tee.Harri
Note that it may fail depending on what /dev/fd/2 exactly is. For example, if you log in as root, your terminal is owned by root. Then, if you switch user, you will have no permission to access /dev/fd/2.Wirra
Also, /proc/self/fd/2 is something may be available. in fact, on my system [CentOS6] /dev/fd is a symlink to /proc/self/fd.Fulmis
NOTE: not awlays has write permision to /dev/fd/2 or /proc/self/fd/2 or /dev/pts/xx. for example: if su to other user (then pts not belong to current uesr)Stickler
C
91

The only cross platform method I found which works in both interactive and non-interactive shells is:

command | tee >(cat 1>&2)

The argument to tee is a file or file handle. Using process substitution we send the output to a process. In the process =cat=, we redirect stdout to stderr. The shell (bash/ksh) is responsible for setting up the 1 and 2 file descriptors.

Confession answered 12/12, 2013 at 20:51 Comment(3)
Make sure to add #!/bin/bash if it's run in a script. As sh does not support >( xxxx )Waites
Shouldn't this be the accepted answer?Augustaaugustan
This is roundabout way, but it always works (in bash). ======== /dev/fd/2 or /proc/self/fd/2 not always exist and has write permission.Stickler
D
65
./script.sh | tee /dev/fd/2

Note that this is dependant on OS support, not any built-in power in tee, so isn't universal (but will work on MacOS, Linux, Solaris, FreeBSD, probably others).

Duvalier answered 10/12, 2012 at 16:32 Comment(8)
For a better explanation of what is taking place behind the scenes in the above command, read the bash hackers site on redirection: wiki.bash-hackers.org/syntax/redirectionBenetta
It's not really redirection, is it? It's just pipe syntax. The /dev/fd/2 argument is opaque to the shell; it only sets up the stdin fd up for the tee child, which is the process that interprets the argument (as a filename, to obtain the output fd with open()).Duvalier
The manpage for bash implies that it accepts /dev/fd/2 as a file name for file descriptor 2 even the file system doesn't have such a file.Harri
@Harri That's interesting. But, in this example the filename isn't ever interpreted by bash, so that isn't coming into play here.Duvalier
Oh, right. It would work with redirection, but not as an argument to tee.Harri
Note that it may fail depending on what /dev/fd/2 exactly is. For example, if you log in as root, your terminal is owned by root. Then, if you switch user, you will have no permission to access /dev/fd/2.Wirra
Also, /proc/self/fd/2 is something may be available. in fact, on my system [CentOS6] /dev/fd is a symlink to /proc/self/fd.Fulmis
NOTE: not awlays has write permision to /dev/fd/2 or /proc/self/fd/2 or /dev/pts/xx. for example: if su to other user (then pts not belong to current uesr)Stickler
S
1

About /dev/stderr permission

user not always has write permission for

  • /dev/stderr
  • /dev/fd/2
  • /proc/self/fd/2
    (they all link to same pts)

for example, after sudo:

sudo su - nobody -s /bin/bash

nobody@test:/$ ls -lh /dev/stderr /dev/fd/2 /proc/self/fd/2
lrwxrwxrwx 1 root   root    15 Jun 10  2021 /dev/stderr -> /proc/self/fd/2
lrwx------ 1 nobody nogroup 64 Apr  7 01:58 /dev/fd/2 -> /dev/pts/5
lrwx------ 1 nobody nogroup 64 Apr  7 01:58 /proc/self/fd/2 -> /dev/pts/5

nobody@test:/$ echo hell >&2
hell

nobody@test:/$ echo hell >/dev/fd/2
-su: /dev/fd/2: Permission denied

nobody@test:/$ echo hell >/dev/stderr 
-su: /dev/stderr: Permission denied

nobody@test:/$ echo hell > /dev/pts/5
-su: /dev/pts/5: Permission denied

Another choice: awk

nobody@test:/$ echo hell | awk '{print>"/dev/stderr";print}' 
hell
hell

nobody@test:/$ echo ' hell   12 ' | awk '{print|"cat 1>&2";print}'
 hell   12 
 hell   12 

Although there has >"/dev/stderr", but it not really same as it in shell.

Maybe awk is not as performant as tee,
But it's worth noting, about the real stderr.

RTSC

Figure out the difference

To be continue..

Stickler answered 6/4, 2023 at 18:20 Comment(1)
This is excellent! Thank you. I just ran into this as I was sudo'ing to a system user and was getting Permission Denied using /dev/stderr ...Allegro
C
-3
./script.sh 2>&1 >/dev/null | tee stderr.out

That opens STDERR to STDOUT, and then disposes of STDOUT.

Chaumont answered 10/12, 2012 at 17:56 Comment(2)
This pipes all output to /dev/null.Tripterous
It does not pipe all to /dev/null, just the script.sh's stdout. The script.sh's stderr goes to tee's stdin and tee writes that all to tee's stdout and the file stderr.out... It is not what was asked for in the question, but it happens to be what I am looking for so +1 from me :-)Kingery

© 2022 - 2024 — McMap. All rights reserved.