7
votes

I understand that stdin and stdout (at least in UNIX parlance) are stream buffers, and that stdout is used to output from a program to the console (or to then be piped by a shell, etc), and that stdin is for standard input to a program..

So why is it, at least on macOS, that they can be used interchangeably (stdout as stdin, and vice versa?

Examples:

  1. If you run cat /dev/stdin then type something in, and it echoes it back. Running the command as cat /dev/stdout does the same thing.

  2. Similarly, echo "Hey There" > /dev/stdout and echo "Hey There" > /dev/stdin both output 'Hey There' back to the terminal.

  3. It also works in C++:

example:

#include <iostream>
#include <string>
#include <fstream>

int main(int argc, const char * argv[]) {
    std::string echoString;
    std::fstream stdoutFile;
    stdoutFile.open("/dev/stdout");
    stdoutFile << "Hey look! I'm using stdout properly!\nNow You trying using it wrongly: " << std::endl;
    stdoutFile >> echoString;
    stdoutFile << "You Typed: " << echoString << std::endl;
}

When prompted, typing a single word, followed by EOF (Ctrl+D) works as expected.

2
Note that the fact that /dev/stdin and /dev/stdout are interchangeable does not imply that stdin and stdout are interchangeable. In particular, if you start a process with > /dev/stdin then it will not be possible to read from the process's standard output stream. - Brian Bi
@Brian what do you mean? This seems to work: pastebin.com/Su3yuJax - TheInnerParty
That's not the same as what I said. std::fstream opens in read/write mode by default. If you do > foo on the terminal, it will open foo in write-only mode and the file descriptor will not be usable for reading. - Brian Bi
@Brian right I get it now. Thanks! - TheInnerParty

2 Answers

3
votes

Because, typically, when a program is invoked from an interactive terminal, with no redirection, both standard input and standard output are connected to the same terminal device, such as /dev/tty (the actual device name varies based on the operating system).

The terminal device is a read/write device. Reading from the terminal device reads terminal input. Writing to the terminal device generates output on the terminal.

You still have discrete file descriptors, 0 and 1, but they're connected to the same device.

Think of it as single, bi-directional pipe, that's duped to both file descriptors 0 and 1.

Linux behaves the same way (you can echo Foo >/dev/stdin and see the output):

$ ls -al /proc/self/fd/[01]
lrwx------. 1 mrsam mrsam 64 Nov 22 21:34 /proc/self/fd/0 -> /dev/pts/1
lrwx------. 1 mrsam mrsam 64 Nov 22 21:34 /proc/self/fd/1 -> /dev/pts/1

So, for this process, file descriptors 0 and 1 is connected to the /dev/pts/1, the same pseudo-terminal device. Whether you are reading from file descriptor 0 or file descriptor 1, you end up reading from the same underlying /dev device, so it makes no difference which actual file descriptor you use.

This is, of course, operating system-dependent. Other POSIX-based operating systems may implement their standard input and output in other ways, where you can't actually write to standard input and read from standard output.

0
votes

As you said, they're just stream buffers. There is nothing about them that enforces a particular usage pattern - just convention. The stream buffers stdin, stdout, and stderr are all provided as a programming convenience.