It comes as a surprise to many people, given that most reference books and documentation always refer to standard error as an output, that it is in fact usually already open for read+write. Your program very much can read from file descriptor 2.
(Note: In this answer I am using the actual file descriptor numbers. The C streams such as stderr
need not actually correspond with these file descriptors, as a program can change them, and it adds a layer of confusion to talk about what the C streams do. Your program is using read()
.)
File descriptor 2 is open for reading and writing
For programs in login sessions where no redirection has been used in a parent process, file descriptor 2 (standard error) is typically a duplicate of file descriptor 0 (standard input). They both reference the same underlying file description, which is typically the terminal of the login session (opened and duplicated by ttymon
or, on older systems, getty
at the very start of the session).
If you read from file descriptor 2, you get the same input as if you had read from file descriptor 0.
BTW: Reading from file descriptor 2 was frequently done for things like password input; before the /dev/tty
device was introduced, around 1977.
The reason for reading from file descriptor 2 was to obtain input from the original terminal when file descriptor 0 had been redirected elsewhere (as is the case in the middle of a pipeline, for example).
Even though /dev/tty
has been available for a long time now, over 40 years, POSIX still requires file descriptor 2 to be open for reading as well.
What your program is not doing
Reading from the file descriptor 2 output of another program is a different thing. You canot easily do that by itself, without merging standard output with standard error. It usually involves a series of 3>&1 1>&2 2>&3
or suchlike swaps. A few shells permit pipes on the output file descriptor 2 by calling
prog1 2| prog2
But such shells are rare, and that's not what your program needs in any case.
Sending input to your program
If you want your program, reading from file descriptor 2, to read from something other than from the terminal, you of course redirect that file descriptor. You could use the normal input redirection syntax (the <
operator in the shell), but libraries in your program, or even other code that you have written elsewhere, will have assumptions that they can write to this file descriptor.
The shell allows you to use the <>
redirect operator that explicitly opens a file for both reading and writing. This is what you use when redirecting your program's file descriptor 2.
./myprogram 2<>filename
Outwith shell redirection, there are plenty of tools that allow manipulation of file descriptors. For example: With Laurent Bercot's chain-loading redirfd
tool, that comes with execline, this redirection looks more like your hypothesis:
redirfd -u 2 filename ./myprogram
There is also shell syntax in the likes of the Bourne Again and Z shells (but not the Almquist shells) to provide "here documents" and "here strings" to file descriptor 2. Note that file descriptor 2 is opened read only by these shells in this case.
./myprogram 2<<< "here string"
STDERR_FILENO
is an output file descriptor. You can't read from it. If you check whatread
returns (which you should always do!) you should probably be getting either an error (-1
) or end-of-file (0
). What is the real problem you're having? Why do you want to read from standard error? – Paulo Tomé Mar 21 '20 at 15:18cat /dev/stderr
works (as doescat /dev/stdout
), and so does that program, well, at least after all the syntax errors and buffer overflows are fixed. – ilkkachu Mar 21 '20 at 15:47stderr
is open for reading and writing and reading fromstderr
was usual before/dev/tty
has been introduced. – schily Mar 21 '20 at 15:51