1. Overview

Redirecting output is a common operation in Linux. For example, we may want to redirect the output of a command to a file for analysis. However, redirecting output is different in Docker than in Linux.

In this tutorial, we’ll discuss ways to redirect command output in Docker.

2. Docker Prerequisites

In an empty directory, let’s create a Dockerfile with the following content:

FROM alpine:latest

CMD ["cat", "/etc/alpine-release"]

Here, we’ve based our Docker container on the Alpine Linux project. Within the container, we display the content of the /etc/alpine-release file using the cat command.

Now, let’s build this Dockerfile:

$ docker build -t dockeroutput  .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM alpine:latest
 ---> c059bfaa849c
Step 2/2 : CMD ["cat", "/etc/alpine-release"]
 ---> Running in 900c4a1f30fe
Removing intermediate container 900c4a1f30fe
 ---> 6e67a72bd2ae
Successfully built 6e67a72bd2ae
Successfully tagged dockeroutput:latest

Next, we’ll run the container:

$ docker run dockeroutput 
3.15.0

The output can be different if the latest version of the Alpine Docker container changes.

3. Docker Command Forms

Commands in Docker can have two forms: the exec form and the shell form.

A command in the exec form looks like this:

CMD ["cat", "/etc/alpine-release"]

By contrast, here’s the same command in the shell form:

CMD cat /etc/alpine-release

There are some differences between these two forms. For example, in the shell form, the $HOME environment variable refers to the home directory. But, in the exec form, $HOME has no significance.

Therefore, redirecting output is also different in these two forms.

4. Executing a Script in the exec Form

Earlier, we used the exec form in our Dockerfile:

CMD ["cat", "/etc/alpine-release"]

Now, suppose we want to redirect the output of the cat command to a file.

We cannot do this:

CMD ["cat", "/etc/alpine-release", ">", "output.txt"]

If we did that, we would get errors:

$ docker run dockeroutput 
3.15.0
cat: can't open '>': No such file or directory
cat: can't open 'output.txt': No such file or directory

We can see that Docker interprets the command in the Dockerfile as if the cat command received three input files: /etc/alpine-release, >, and output.txt.

That’s why cat was executed successfully for the first input file, as indicated by this line in the output:

3.15.0

However, since there are no files named > and output.txt, we get the errors above.

4.1. Changing the Dockerfile

The exec form doesn’t recognize the > sign as the special character to redirect output. Instead, it interprets the sign as a file name.

To redirect the command’s output in exec form, we need to wrap it in a script.

Let’s create a script named check_alpine_release.sh with the following content:

#!/bin/sh

cat /etc/alpine-release > /home/output.txt

In bash scripts, we can freely use the > sign to redirect command output.

Next, we’ll modify the Dockerfile:

FROM alpine:latest

COPY check_alpine_release.sh .

RUN chmod +x /check_alpine_release.sh

CMD ["/check_alpine_release.sh"]

Here, we copied our bash script from the host to the container. Then, we set the file to be an executable file. Finally, we execute the bash script. This way, we can avoid the > sign in the Dockerfile.

4.2. Running the Container

After building the Dockerfile, let’s run the container:

$ docker run -v $(pwd):/home dockeroutput

We run the container using the v option so that we can map our current directory to the home directory inside the container. This way, we can provide the output file that accepts the stdout output from the command. In other words, we’ve redirected the command output to the output.txt file inside the home directory.

We won’t get any output when running the container. But, we can see that now there’s a output.txt in the directory.

Let’s look at the file’s content:

$ cat output.txt 
3.15.0

5. Using the shell Form

We don’t have to write a bash script to redirect the output. Instead, we can redirect the command in Docker with the shell form using the > special character in the command.

5.1. Changing the Dockerfile

Let’s rewrite the Dockerfile so it has the following content:

FROM alpine:latest

CMD cat /etc/alpine-release > /home/output.txt

We’ve used the shell form to execute the command that redirects the output to a file.

5.2. Running the Container

Now, we can build the Dockerfile and run the container with the v option:

$ docker run -v $(pwd):/home dockeroutput

We get the same result:

$ cat output.txt 
3.15.0

6. Passing in the Shell in the exec Form

Another option is to use the shell in the exec form. We can also choose our desired shell using this way.

6.1. Changing the Dockerfile

To use the shell in the exec form, we can write the Dockerfile like this:

FROM alpine:latest

CMD ["/bin/sh", "-c", "cat /etc/alpine-release > /home/output.txt"]

It uses the shell as the main command to execute another command that we want. Further, the c option of the shell command reads the command from the argument string, instead of the standard input.

Alternatively, we can use a different shell, for example:

CMD ["/bin/bash", "-c", "cat /etc/alpine-release > /home/output.txt"]

We should note that the command cannot be separated:

CMD ["/bin/sh", "-c", "cat", "/etc/alpine-release", ">", "/home/output.txt"]

If we separated the command, the invocation would look like this:

$ /bin/sh -c cat etc/alpine-release > /home/output.txt

The shell only reads cat as the command string and ignores the remaining arguments. So, it’s the same as this command invocation:

$ /bin/sh -c cat

The correct command invocation is like this:

$ /bin/sh -c "cat etc/alpine-release > /home/output.txt"

Hence, we should not separate the command string.

6.2. Running the Container

Now, we can build the Dockerfile and run the container with the v option:

$ docker run -v $(pwd):/home dockeroutput

We get the same result:

$ cat output.txt 
3.15.0

7. Redirecting Both stdout and stderr

The command’s output in the container can be in stdout or stderr. We can redirect the stdout output and the stderr output into two separate files.

To demonstrate this, we’ll display the content of two files using the cat command. The first file is /etc/alpine-release and the second is a non-existent one called  file_does_not_exist.txt.

cat displays the content of the first file in stdout, and for the second file, it displays the error in stderr.

Let’s rewrite the Dockerfile:

FROM alpine:latest

CMD cat /etc/alpine-release file_does_not_exist.txt > /home/output.txt 2>/home/error.txt

While the > /home/output.txt part redirects the stdout output to a file, the 2>/home/error.txt part redirects the stderr output to another file.

Let’s build the Dockerfile and run the container:

$ docker run -v $(pwd):/home dockeroutput

Now, we get both the output.txt file and the error.txt file:

$ cat output.txt 
3.15.0
$ cat error.txt 
cat: can't open 'file_does_not_exist.txt': No such file or directory

8. Filtering the Output

By default, we get the stdout output and the stderr output from the container. But, we can choose to only listen to stdout or stderr only if there is no need to listen to both of them.

Let’s write the Dockerfile with the following content:

FROM alpine:latest

CMD cat /etc/alpine-release file_does_not_exist.txt

Next, we build the Dockerfile and run the container:

$ docker run dockeroutput 
3.15.0
cat: can't open 'file_does_not_exist.txt': No such file or directory

But what if we only want to get the stderr output and ignore the stdout output?

We can do that with the a option:

$ docker run -a stderr dockeroutput 
cat: can't open 'file_does_not_exist.txt': No such file or directory

The a option is used to attach to the standard input, the standard output, or the standard error.

9. Conclusion

In this article, we learned how to redirect the output of a command in a Docker container.

First, we wrote a bash script as a wrapper to redirect the output when running the command in the exec form. Then, we used the > sign to redirect the output when running the command in the shell form.

Finally, we also learned how to filter and redirect both stdout and stderr in the container.