Quick Links

Key Takeaways

  • Linux commands create three data streams (stdin, stdout, and stderr) that can be used to transfer data about a command
  • stdin is the input stream, stdout is the output stream, and stderr is the error stream in Linux.
  • Redirection allows you to redirect the output or errors to different destinations, such as files or pipes.

stdin, stdout, and stderr are three data streams created when you launch a Linux command. You can use them to tell if your scripts are being piped or redirected. We show you how.

Streams Join Two Points

As soon as you start to learn about Linux and Unix-like operating systems, you'll come across the terms stdin, stdout, and stederr . These are three standard streams that are established when a Linux command is executed. In computing, a stream is something that can transfer data. In the case of these streams, that data is text.

Data streams, like water streams, have two ends. They have a source and an outflow. Whichever Linux command you're using provides one end of each stream. The other end is determined by the shell that launched the command. That end will be connected to the terminal window, connected to a pipe, or redirected to a file or other command, according to the command line that launched the command.

The Linux Standard Streams: stdin, stdout, stderr

In Linux, stdin is the standard input stream. This accepts text as its input. Text output from the command to the shell is delivered via the stdout (standard out) stream. Error messages from the command are sent through the stderr (standard error) stream.

So you can see that there are two output streams, stdout and stderr, and one input stream, stdin. Because error messages and normal output each have their own conduit to carry them to the terminal window, they can be handled independently of one another.

Streams Are Handled Like Files

Streams in Linux---like almost everything else---are treated as though they were files. You can read text from a file, and you can write text into a file. Both of these actions involve a stream of data. So the concept of handling a stream of data as a file isn't that much of a stretch.

Each file associated with a process is allocated a unique number to identify it. This is known as the file descriptor. Whenever an action is required to be performed on a file, the file descriptor is used to identify the file.

These values are always used for stdin, stdout, and stderr:

  • 0: stdin
  • 1: stdout
  • 2: stderr

Reacting to Pipes and Redirects

To ease someone's introduction to a subject, a common technique is to teach a simplified version of the topic. For example, with grammar, we are told that the rule is "I before E, except after C." But actually, there are more exceptions to this rule than there are cases that obey it.

In a similar vein, when talking about stdin, stdout, and stderr it is convenient to trot out the accepted axiom that a process neither knows nor cares where its three standard streams are terminated. Should a process care whether its output is going to the terminal or being redirected into a file? Can it even tell if its input is coming from the keyboard or is being piped into it from another process?

Actually, a process does know---or at least it can find out, should it choose to check---and it can change its behavior accordingly if the software author decided to add that functionality.

We can see this change in behavior very easily. Try these two commands:

ls

ls in a terminal window

ls | cat

ls output in a terminal window

The ls command behaves differently if its output (stdout) is being piped into another command. It is ls that switches to a single column output, it isn't a conversion performed by cat. And ls does the same thing if its output is being redirected:

ls > capture.txt

ls > capture.txt in a terminal window

cat capture.txt

cat capture.txt in a terminal window

Redirecting stdout and stderr

There's an advantage to having error messages delivered by a dedicated stream. It means we can redirect a command's output (stdout) to a file and still see any error messages (stderr) in the terminal window. You can react to the errors if you need to, as they occur. It also stops the error messages from contaminating the file that stdout has been redirected into.

Type the following text into an editor and save it to a file called error.sh.

        #!/bin/bash
echo "About to try to access a file that doesn't exist"
cat bad-filename.txt

Make the script executable with this command:

chmod +x error.sh

The first line of the script echoes text to the terminal window, via the stdout stream. The second line tries to access a file that doesn't exist. This will generate an error message that is delivered via stderr.

Run the script with this command:

./error.sh

./error.sh in a terminal window

We can see that both streams of output, stdout and stderr, have been displayed in the terminal windows.

output from error.sh script in a terminal window

Let's try to redirect the output to a file:

./error.sh > capture.txt

./error.sh > capture.txt in a terminal window

The error message that is delivered via stderr is still sent to the terminal window. We can check the contents of the file to see whether the stdout output went to the file.

cat capture.txt

cat capture.txt in a terminal window

The output from stdin was redirected to the file as expected.

contents of capture.txt in a terminal window

The > redirection symbol works with stdout by default. You can use one of the numeric file descriptors to indicate which standard output stream you wish to redirect.

To explicitly redirect stdout, use this redirection instruction:

1>

To explicitly redirect stderr, use this redirection instruction:

2>

Let's try to our test again, and this time we'll use 2>:

./error.sh 2> capture.txt

Error.sh output doesn't exist.

The error message is redirected and the stdout echo message is sent to the terminal window:

stdout is sent to the Terminal.

Let's see what is in the capture.txt file.

cat capture.txt

cat capture.txt in a terminal window

The stderr message is in capture.txt as expected.

Capture.txt has the contents we would expect.

Redirecting Both stdout and stderr

Surely, if we can redirect either stdout or stderr to a file independently of one another, we ought to be able to redirect them both at the same time, to two different files?

Yes, we can. This command will direct stdout to a file called capture.txt and stderr to a file called error.txt.

./error.sh 1> capture.txt 2> error.txt

Sending stderr and stdout to two separate files.

Because both streams of output--standard output and standard error---are redirected to files, there is no visible output in the terminal window. We are returned to the command line prompt as though nothing has occurred.

The Terminal will display nothing, since the output is redirected.

Let's check the contents of each file:

cat capture.txt

cat error.txt

Our two text files, error.txt and capture.txt, contain the correct information.

Redirecting stdout and stderr to the Same File

That's neat, we've got each of the standard output streams going to its own dedicated file. The only other combination we can do is to send both stdout and stderr to the same file.

We can achieve this with the following command:

./error.sh > capture.txt 2>&1

Let's break that down.

  • ./error.sh: Launches the error.sh script file.
  • > capture.txt: Redirects the stdout stream to the capture.txt file. > is shorthand for 1>.
  • 2>&1: This uses the &> redirect instruction. This instruction allows you to tell the shell to make one stream got to the same destination as another stream. In this case, we're saying "redirect stream 2, stderr, to the same destination that stream 1, stdout, is being redirected to."
A concise way to redirect both outputs to one file.

There is no visible output. That's encouraging.

Once again, no output in the Terminal.

Let's check the capture.txt file and see what's in it.

cat capture.txt

Capture.txt correctly contains the output of both stderr and stdout.

Both the stdout and stderr streams have been redirected to a single destination file.

To have the output of a stream redirected and silently thrown away, direct the output to /dev/null.

Detecting Redirection Within a Script

We discussed how a command can detect if any of the streams are being redirected, and can choose to alter its behavior accordingly. Can we accomplish this in our own scripts? Yes, we can. And it is a very easy technique to understand and employ.

Type the following text into an editor and save it as input.sh.

        #!/bin/bash
if [ -t 0 ]; then
echo stdin coming from keyboard
else echo stdin coming from a pipe or a file
fi

Use the following command to make it executable:

chmod +x input.sh

The clever part is the test within the square brackets. The -t (terminal) option returns true (0) if the file associated with the file descriptor terminates in the terminal window. We've used the file descriptor 0 as the argument to the test, which represents stdin.

If stdin is connected to a terminal window the test will prove true. If stdin is connected to a file or a pipe, the test will fail.

We can use any convenient text file to generate input to the script. Here we're using one called dummy.txt.

./input.sh < dummy.txt

Directing input from a script.

The output shows that the script recognizes that the input isn't coming from a keyboard, it is coming from a file. If you chose to, you could vary your script's behavior accordingly.

Correctly recognizes that it is coming from a pipe or a file.

That was with a file redirection, let's try it with a pipe.

cat dummy.txt | ./input.sh

28-1

The script recognizes that its input is being piped into it. Or more precisely, it recognizes once more that the stdin stream is not connected to a terminal window.

Correctly recognizes that it is coming from a pipe or a file.

Let's run the script with neither pipes nor redirects.

./input.sh

Running input script.

The stdin stream is connected to the terminal window, and the script reports this accordingly.

To check the same thing with the output stream, we need a new script. Type the following into an editor and save it as output.sh.

        #!/bin/bash
if [ -t 1 ]; then
echo stdout is going to the terminal window
else
echo stdout is being redirected or piped
fi

Use the following command to make it executable:

chmod +x input.sh

The only significant change to this script is in the test in the square brackets. We're using the digit 1 to represent the file descriptor for stdout.

Let's try it out. We'll pipe the output through cat.

./output | cat

32-1

The script recognizes that its output is no going directly to a terminal window.

Output is being piped out of the Terminal window.

We can also test the script by redirecting the output to a file.

./output.sh > capture.txt

Outputting to capture.txt

There is no output to the terminal window, we are silently returned to the command prompt. As we'd expect.

Another blank Terminal window resulting from the output being sent to a text file.

We can look inside the capture.txt file to see what was captured. Use the following command to do so.

cat capture.sh

Capture.txt contains the report that the command has been piped.

Again, the simple test in our script detects that the stdout stream is not being sent directly to a terminal window.

If we run the script without any pipes or redirections, it should detect that stdout is being delivered directly to the terminal window.

./output.sh

Running output.sh with no arguments.

And that's exactly what we see.

stdout is going to the Terminal.

Streams Of Consciousness

Knowing how to tell if your scripts are connected to the terminal window, or a pipe, or are being redirected, allows you to adjust their behavior accordingly.

Logging and diagnostic output can be more or less detailed, depending on whether it is going to the screen or to a file. Error messages can be logged to a different file than the normal program output.

As is usually the case, more knowledge brings more options.

Linux Commands

Files

tar · pv · cat · tac · chmod · grep · diff · sed · ar · man · pushd · popd · fsck · testdisk · seq · fd · pandoc · cd · $PATH · awk · join · jq · fold · uniq · journalctl · tail · stat · ls · fstab · echo · less · chgrp · chown · rev · look · strings · type · rename · zip · unzip · mount · umount · install · fdisk · mkfs · rm · rmdir · rsync · df · gpg · vi · nano · mkdir · du · ln · patch · convert · rclone · shred · srm · scp · gzip · chattr · cut · find · umask · wc · tr

Processes

alias · screen · top · nice · renice · progress · strace · systemd · tmux · chsh · history · at · batch · free · which · dmesg · chfn · usermod · ps · chroot · xargs · tty · pinky · lsof · vmstat · timeout · wall · yes · kill · sleep · sudo · su · time · groupadd · usermod · groups · lshw · shutdown · reboot · halt · poweroff · passwd · lscpu · crontab · date · bg · fg · pidof · nohup · pmap

Networking

netstat · ping · traceroute · ip · ss · whois · fail2ban · bmon · dig · finger · nmap · ftp · curl · wget · who · whoami · w · iptables · ssh-keygen · ufw · arping · firewalld