1. Overview
In Linux, the default behavior of variable assignment is to store the standard output of a command or a program to a variable. However, commands could be writing error messages to standard error that we might want to store for further analysis.
In this tutorial, we’ll learn multiple ways to store standard error messages in a Linux variable.
2. Scenario Setup
First, let’s write a demo.sh Bash script that contains logic to produce both standard output and standard error:
$ cat demo.sh
#!/bin/bash
echo "This is a message on stdout"
echo "This is an error message on stderr" >&2
echo "This is another message on stdout"
echo "This is another error message on stderr" >&2
It’s important to note that we’ve used a redirection (>&2) to write to the stderr.
Now, let’s execute our demo.sh script to see a sample run:
$ ./demo.sh
This is a message on stdout
This is an error message on stderr
This is another message on stdout
This is another error message on stderr
It’s interesting to note that both stdout and stderr print to the console. So, it’s not straightforward to identify which message on the screen is part of stdout and which is part of stderr.
Lastly, let’s use redirections to see stdout and stderr in separate executions of the demo.sh script:
$ ./demo.sh 1>/dev/null
This is an error message on stderr
This is another error message on stderr
$ ./demo.sh 2>/dev/null
This is a message on stdout
This is another message on stdout
Great! We can now see them separately on the console. So, let’s get started on our primary use case of storing only stderr in a variable.
3. Using a Temporary File
First, let’s redirect the stderr to the /tmp/error.txt temporary file:
$ ./demo.sh 2> /tmp/error.txt
This is a message on stdout
This is another message on stdout
Next, let’s use the cat command to initialize the error variable with the contents from the /tmp/error.txt file:
error=$(cat /tmp/error.txt)
Finally, let’s use the echo command to verify the value in the error variable:
$ echo "$error"
This is an error message on stderr
This is another error message on stderr
Fantastic! It works.
4. Command Substitution With Redirection
Instead of using an intermediate placeholder for storing the stderr, we can directly use command substitution with redirections:
$ error=$(./demo.sh 2>&1 >/dev/null)
We’ve used >/dev/null and 2>&1 redirections to discard stdout and send the stderr to stdout, respectively.
Now, let’s also verify the contents of the error variable:
$ echo "$error"
This is an error message on stderr
This is another error message on stderr
The result looks correct.
5. Using the tee Command
The tee utility in Linux is a de facto choice for handling output produced by commands and processes.
Using the tee command, not only can we store the message from stderr into a variable, but we can also duplicate it on the console:
$ error=$(./demo.sh 2>&1 >/dev/null | tee /dev/stderr)
This is an error message on stderr
This is another error message on stderr
We can notice that the stderr-specific messages are shown on the console. Nevertheless, let’s also verify the contents of the error variable to confirm the overall correctness of our approach:
$ echo "$error"
This is an error message on stderr
This is another error message on stderr
Great! It looks like we’ve nailed this.
6. Command Grouping
Using command grouping, we can apply redirections to an entire list of commands in a single go. In this section, we’ll learn how to solve our use case with the help of command grouping.
6.1. With the sed Command
Let’s start by using the sed command in our command group and redirecting the stderr to stdout using 2>&1:
$ error=$( { ./demo.sh | sed -n -e '';} 2>&1)
Further, let’s note that we’re using the -n flag in the sed command to discard the stdout. As a result, we’re only left with stderr.
Now, let’s also verify that our approach worked correctly:
$ echo "$error"
This is an error message on stderr
This is another error message on stderr
It looks correct.
6.2. With the awk Command
Similarly, we can also use the awk command in the command group:
$ error=$({ ./demo.sh | awk '{print >/dev/null}' ;} 2>&1)
We can see that we’ve used the print function in the awk script that uses /dev/null to discard the standard output. Unlike the first approach with the sed command, we need to do this explicitly in case of awk. In the end, we’re left with only stderr.
Again, let’s verify the contents of the error variable:
$ echo "$error"
This is an error message on stderr
This is another error message on stderr
Perfect! We’ve got this one right.
6.3. With the grep Command
Let’s take the grep command as another alternative for the command grouping to solve our use case:
$ error=$({ ./demo.sh >/dev/null | grep '' >/dev/null ;} 2>&1)
We can see that we’ve used an empty pattern as a catch-all mechanism to match all lines. Further, we’ve discarded stdout using the >/dev/null redirection. Lastly, we used the 2>&1 redirection to grab the contents from stderr.
Now, let’s verify the correctness of our approach:
$ echo "$error"
This is an error message on stderr
This is another error message on stderr
That’s it. We’ve got a good understanding of the concept now.
7. Conclusion
In this tutorial, we learned how to store standard errors in a Linux variable. Further, we solved the use case with multiple approaches, such as command substitution, process substitution, the tee command, and a temporary file.
Finally, we also explored using command groups with popular utilities, such as sed, awk, and grep, to solve our use case.