1. Overview

When using commands, we may wish to pass the output of one command into another. This is most commonly achieved using pipes, but when scripting, we may prefer to store the output in a variable so we can process it.

In this tutorial, we’ll look at how to capture the output of a command in a variable. We’ll look at doing this on an ad-hoc basis and how to create some longer-lasting solutions to make it easy for us to refer back to the output of previous commands.

2. Use Cases

Let’s start by looking at a couple of commands with different sizes of outputs. The wc command outputs the count of words in a file, which is a scalar value. The ls command outputs a list of values, which we may then wish to use with mv to move the files somewhere.

While the solutions presented are compatible with both use cases, the difference between storing a single value or a list is in how we use the resulting variable.

2.1. Change the Directory Based on the Number of Word of a File

The first use case deals with storing the output of the wc -w command:

$ wc -w < file_with_5_words
5

In this case, the file file_with_5_words has 5 words.

Let’s imagine we want to change to the directory whose name corresponds to that output:

$ cd 5

2.2. Move Certain Files Based on the Contents of a Folder

The second use case is to move a list of files (those whose extension is txt) to a directory. To do so, we would first need to list the files:

$ ls *.txt
ax.txt ay.txt

The syntax of the mv command is:

mv file1 file2 ... targetDir

This means that the command accepts multiple files as input at the same time.

3. Ad-Hoc Solutions

These solutions don’t require the creation of functions or extra files. They’re ideal for occasional use.

By default, the shell stores no information about the output of a command. We can refer back to the exit code of the last command. We can also run the history command to see the command lines of each operation. Thus, we need to run the command a certain way to capture its output.

3.1. Explicitly Using Variables

We can assign a variable to contain the output to stdout of a command:

$ var=$(wc -w < file_with_5_words)

This can also be achieved using backward quotes:

$ var=`wc -w < file_with_5_words`

The old-style backward quote substitution doesn’t preserve the meaning of special characters like $, \ or the actual ` itself. These characters require escaping with a backslash. The syntax $(…) treats no character in any special way.

With both of these approaches, we’re storing the output of the wc -w in a variable named var. We can then use the variable by prefixing the dollar sign before it in a command:

$ cd $var

The solution for the second use case is similar, except that the variable contains multiple values. Let’s check that with the echo command:

$ var=`ls *.txt`
$ echo $var
ax.txt ay.txt

After checking the content of the variable with echo and seeing that it has two files. Let’s now use it with mv:

$ mv $var 5

3.2. What If We Use the Variables Incorrectly?

Shell variables don’t have a type. We should act as though all variables were strings. Thus, we need to be careful to use the contents of a variable only with commands that can accept them.

For example, let’s see what happens if we use the output from ls (which is a list) with the cd command:

$ var=$(ls *.txt)
$ echo $var
ax.txt ay.txt
$ cd $var
-bash: cd: too many arguments

Here, the cd command failed because it accepts only one input value and not multiple values separated with spaces.

3.3. Using History Expansion – !!

Though the shell does not track the output of a command, we can use history expansion to re-run a previous command to assign its output to a variable:

$ ls *.txt
ax.txt ay.txt
$ var=$(!!)
var=$(ls *.txt)
$ echo $var
ax.txt ay.txt

Similarly, we can use the number from the output of history to re-run an earlier command:

$ history
 1097  git log
 1098  ls *.txt
$ var=$(!1098)

We should note that this approach may produce unwanted results. For example, if the output of the command changes on each invocation, then our variable will contain the output of the most recent call.

4. Longer-Term Solutions

If we’re capturing command output a lot, we may wish to set up some scripting on our system to help us.

4.1. Using a Bash Function

Let’s define a function, out2var, to capture command output and put it in var:

out2var() {
  var=$("$@")
  printf '%s\n'$var
}

This takes the command line to execute via the input parameter @ and then stores the output in var. As we want to see the output, we have a printf call.

Every time the function out2var is invoked, the variable var is assigned:

$ out2var wc -w < file_with_5_words
5
$ echo $var
5
$ cd $var

We may put this function into our current terminal or even add it to our .bashrc file.

4.2. Using a Temporary File and an Environmental Variable

Rather than run a special wrapper on our commands, we can implement a hook to store the output of every command into a file.

This solution uses environmental variables:

$ PROMPT_COMMAND='touch /tmp/last_command; var="$(cat /tmp/last_command)"; exec >/dev/tty; exec > >(tee /tmp/last_command)'

First, we create the temporary file and set the variable name, var, that we will invoke later to retrieve the output. Then, it prints the content of the temporary file located in /tmp/last_command. After the first semicolon, we execute the command with exec in the current console terminal (represented by /dev/tty). Finally, after the last semicolon, the tee command with the pipe sends the standard output to the temporary file and also to the screen.

The use is relatively straightforward, as, after any given command, the output is automatically stored:

$ ls *.txt
ax.txt
ay.txt
$ echo $var
ax.txt ay.txt
$ mv $var 5

This should be the preferred approach if we want to automate the process of capturing outputs. However, this approach has two drawbacks:

  • We should remember that, as the procedure is automatic, we cannot rely on *$*var multiple times. As opposed to before, where we needed to explicitly invoke out2var, here we replace the content of $var every time we execute one command.
  • It captures the output of every command, which may cause problems with interactive commands (for example, vim).

To stop the automatic storage of the outputs, we can run:

$ unset PROMPT_COMMAND

5. Conclusion

In this article, we discussed different ways of storing the output of a command in a variable.

We saw how to use this variable as part of the input for another command.

We also looked at some scripts we can create to help capture command output more frequently.