Bash STDOUT File Descriptors – How Bash Gets the Output of an Executed Program

bashfile-descriptorsstdout

When I execute a program in Bash, for example, [ls][2], it sends its output to standard output (fd &1). And the ouput of the executed program is displayed in the terminal. How does Bash/terminal get the output of the ls command?

Best Answer

As a child process of the shell, ls inherits the open file descriptors of the shell. And the standard file descriptors (stdin, stdout, stderr (or 0, 1, 2)) are connected to a pseudo-terminal, which is handled by the terminal emulator.

For example (on a Linux system):

$ ls /proc/$$/fd -l
total 0
lrwx------ 1 muru muru 64 Dec 10 16:15 0 -> /dev/pts/3
lrwx------ 1 muru muru 64 Dec 10 16:15 1 -> /dev/pts/3
lrwx------ 1 muru muru 64 Dec 10 16:15 2 -> /dev/pts/3
lrwx------ 1 muru muru 64 Dec 10 16:15 255 -> /dev/pts/3
$ ls /proc/$(pgrep terminator -f)/fd -l | grep pts/3
lrwx------ 1 muru muru 64 Dec 10 16:15 26 -> /dev/pts/3

That is, the output of ls, or for that matter the shell itself, is not handled by the shell, but by the terminal emulator (GNOME Terminal, terminator, xterm, etc.).


You can test this out:

On Linux, find a pseudoterminal (pts) used by your terminal emulator (say GNOME Terminal):

$ ls -l /proc/$(pgrep -n gnome-terminal)/fd | grep pts
lrwx------ 1 muru muru 64 Dec 10 18:00 1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Dec 10 18:00 15 -> /dev/pts/20
lrwx------ 1 muru muru 64 Dec 10 18:00 2 -> /dev/pts/1

Now, the non-standard fds (those other than 0,1,2) of gnome-terminal would be used by it to provide input and output for a shell. The terminal emulator reads in data send to that PTS and (after some processing, for colours and such) presents it on the screen. In this case, that would be 15, connected to pts/20. If I write something to that pts, I can expect it to appear in that terminal:

enter image description here

Further reading:


The other case, where I do things like:

echo $(ls)
a=$(date)
vim `command -v some_script`

is called Command Substitution. In command substitution, the output of the command is captured by the shell itself, and never reaches the terminal, unless you do print it out (for example, echo $(ls)). This case is handled in Hauke Laging's answer.

Related Question