Unix streams
Unix streams were invented to act like garden hoses, piping data between programs on a computer.
In Unix, there are three “standard” streams:
stdin
- standard input, or the input data for a programstdout
- standard output, or the output data from a programstderr
- standard error, or any error-related output data from a program
Everything is a file in Unix, meaning that Unix is simply composed of files communicating with each other via streams. Consider the TTY, which uses a file to represent each TTY instance and handle its input and output:
tty
command prints the filepath to the file that represents the current TTY instance:
$ tty
/dev/ttys001
This file receives stdin
from any input device drivers via the terminal runtime and pipes that input data through stdout
to programs and/or files on the machine, which may or may not use the TTY’s stdin
stream to send output back up through the TTY to the terminal instance.
An interesting experiment is invoking the cat
command without any stdin
:
$ cat
Everything I write before pressing Enter
Everything I write before pressing Enter
Gets logged right after
Gets logged right after
Interestingly, the same thing happens when cat
is sent the filepath to the tty
file as stdin
:
$ cat $(tty)
Everything I write before pressing Enter
Everything I write before pressing Enter
Gets logged right after
Gets logged right after
This happens because cat
simply reads the contents of a file at a given filepath and writes its stdout
to the stdin
stream of the TTY instance, unless cat
is specifically sent somewhere else. Essentially, a pipe (|
) changes the destination of the preceding program’s stdout
. So, all programs write their stdout
to the /dev/tty
file by default, unless piped somewhere else instead.
Piping tty
to cat
simply prints the stdout
of the tty
command:
$ tty | cat
/dev/ttys001
Redirection
Input/output data can be redirected as well using the input/output redirection operators. Redirection operators allow for reading from and writing to files.
>
- output redirection (write to, will overwrite entire file)$ cat file1.txt > file2.txt
>>
- output redirection (append to)$ cat file1more.txt >> file2.txt
<
- input redirection (read from stdin) (note the output redirection at the beginning)$ > file2.txt < file1more.txt
<<
- here document (AKA, heredoc) (delimiter can be anything, not just ‘EOF’)$ > file2.txt << EOF this is a stream of input terminated by the custom delimiter EOF $ cat file2.txt this is a stream of input terminated by the custom delimiter
Since there are two standard output streams (stdout
and stderr
), output can be redirected selectively by preceding output redirection with a file descriptor for the desired standard output stream. The file descriptor for stdout
is 1
, so the command cat file1.txt 1> file2.txt
will only redirect the stdout
output stream. This is the same as >
without a file descriptor; 1
is the default. To redirect only the stderr
stream, use 2
. To redirect both stdout
and stderr
, use &
. Like stdout
, stderr
redirects back to the TTY directly by default.
Redirecting between TTY instances
Combining these redirection concepts with the fact that the TTY is just a file allows for redirecting output to other TTY instances, provided you know the proper TTY file to redirect to:
TERMINAL 1
$ tty
/dev/ttys001
$ echo "Hello terminal 2!" > /dev/ttys002
TERMINAL 2
$ tty
/dev/ttys002
$ Hello terminal 2!
Further reading
- A must-read article on Bash redirections: Bash One-Liners Explained, Part III: All about redirections ↗
- Linux
man
page for pipes: link ↗