You might be tired of hearing it, but it is still no less true: in Linux, everything is a file. We've seen that a file is a file, a directory is a file, even hard disks are files; but now, we'll take this one step further: your keyboard, which you use for input, is also a file!
Complementary to that, your Terminal, which commands use as output, is, guess what: a file.
You can find these files, as with most special files, within your Linux filesystem tree. Let's check our virtual machine:
reader@ubuntu:~$ cd /dev/fd/
reader@ubuntu:/dev/fd$ ls -l
total 0
lrwx------ 1 reader reader 64 Nov 5 18:54 0 -> /dev/pts/0
lrwx------ 1 reader reader 64 Nov 5 18:54 1 -> /dev/pts/0
lrwx------ 1 reader reader 64 Nov 5 18:54 2 -> /dev/pts/0
lrwx------ 1 reader reader 64 Nov 5 18:54 255 -> /dev/pts/0
Out of the four files we find here, three are important: /dev/fd/0, /dev/fd/1, and /dev/fd/2.
As you might suspect from the heading of this text, fd stands for file descriptor. These file descriptors are used internally to bind input and output from and to the user to a Terminal. You can actually see how this is done with the file descriptors: they are symbolically linked to /dev/pts/0.
In this instance, pts stands for pseudo Terminal slave, which is the definition given to SSH connections. Look at what happens when we look at /dev/fd from three different locations:
# SSH connection 1
reader@ubuntu:~/scripts/chapter_12$ ls -l /dev/fd/
total 0
lrwx------ 1 reader reader 64 Nov 5 19:06 0 -> /dev/pts/0
lrwx------ 1 reader reader 64 Nov 5 19:06 1 -> /dev/pts/0
lrwx------ 1 reader reader 64 Nov 5 19:06 2 -> /dev/pts/0
# SSH connection 2
reader@ubuntu:/dev/fd$ ls -l
total 0
lrwx------ 1 reader reader 64 Nov 5 18:54 0 -> /dev/pts/1
lrwx------ 1 reader reader 64 Nov 5 18:54 1 -> /dev/pts/1
lrwx------ 1 reader reader 64 Nov 5 18:54 2 -> /dev/pts/1
# Virtual machine terminal
reader@ubuntu:/dev/fd$ ls -l
total 0
lrwx------ 1 reader reader 64 Nov 5 19:08 0 -> /dev/tty/1
lrwx------ 1 reader reader 64 Nov 5 19:08 1 -> /dev/tty/1
lrwx------ 1 reader reader 64 Nov 5 19:08 2 -> /dev/tty/1
Each of these connections has their own /dev/ mount (which is of udev type, stored in memory), which is why we do not see output from one connection into the other one.
Now, we've been talking about input and output. But, as you have no doubt seen, there are three file descriptors allocated in the preceding examples. In a Linux (or Unix/Unix-like system), there are three default streams which are exposed by default through file descriptors:
- The standard input stream, stdin, by default bound to /dev/fd/0
- The standard output stream, stdout, by default bound to /dev/fd/1
- The standard error stream, stderr, by default bound to /dev/fd/2
As far as these three streams go, stdin and stdout should be rather straightforward: input and output. However, as you might have deduced, output is actually split into normal output and error output. Normal output is sent to the stdout file descriptor, while error output is often sent to stderr.
Since both of these are symbolically linked to the Terminal, you will see them there no matter what. However, as we will see later on in this chapter, as soon as we start redirecting, this difference becomes important.
In a normal interaction, text you type in a Terminal gets written to stdin on /dev/fd/0, which a command can read. Using that input, the command usually does something (otherwise, we wouldn't need the command!) and writes the output to stdout or stderr. where it will be read by the Terminal for display to you. So in short:
- A Terminal writes to stdin and reads from stdout or stderr
- A command reads from stdin and writes to stdout or stderr