Contents

如何在Docker中重定向命令输出

1. 概述

重定向输出 是 Linux 中的常见操作。例如,我们可能希望将命令的输出重定向到文件进行分析。但是,在Docker中重定向输出与在 Linux 中不同。

在本教程中,我们将讨论在 Docker 中重定向命令输出的方法。

2. Docker 先决条件

在一个空目录中,让我们创建一个包含以下内容的 Dockerfile:

FROM alpine:latest
CMD ["cat", "/etc/alpine-release"]

在这里,我们的 Docker 容器基于 Alpine Linux 项目。在容器中,我们使用**cat 命令显示*/etc/alpine-release*文件**的内容。 现在,让我们构建这个 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

接下来,我们将运行容器:

$ docker run dockeroutput 
3.15.0

如果最新版本的 Alpine Docker 容器发生更改,输出可能会有所不同。

3. Docker 命令表单

Docker 中的命令可以有两种形式:exec形式和shell形式。 exec形式的命令如下所示:

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

相比之下,这是shell形式的相同命令:

CMD cat /etc/alpine-release

这两种形式之间存在一些差异。例如,在shell形式中,$HOME环境变量指的是主目录。但是,在exec形式中,$HOME没有意义。

因此,这两种形式的重定向输出也是不同的。

4. 在exec表单中执行脚本

之前,我们在 Dockerfile 中使用了exec形式:

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

现在,假设我们要将cat命令的输出重定向到一个文件。 我们不能这样做:

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

如果我们这样做,我们会得到错误:

$ 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

我们可以看到 Docker 将 Dockerfile 中的命令解释为好像cat命令接收了三个输入文件:/etc/alpine-release>output.txt

这就是cat成功执行第一个输入文件的原因,如输出中的这一行所示:

3.15.0

但是,由于没有名为*>output.txt*的文件,我们得到了上面的错误。

4.1. 更改 Dockerfile

exec表单无法将 > 符号识别为重定向输出的特殊字符。相反,它将符号解释为文件名。

要以exec形式重定向命令的输出,我们需要将其包装在脚本中。

让我们创建一个名为check_alpine_release.sh的脚本,其内容如下:

#!/bin/sh
cat /etc/alpine-release > /home/output.txt

在 bash 脚本中,我们可以自由地使用*>*符号来重定向命令输出。

接下来,我们将修改 Dockerfile:

FROM alpine:latest
COPY check_alpine_release.sh .
RUN chmod +x /check_alpine_release.sh
CMD ["/check_alpine_release.sh"]

在这里,我们将 bash 脚本从主机复制到容器。然后,我们将文件设置为可执行文件。最后,我们执行 bash 脚本。这样,我们可以避免 Dockerfile 中的*>*符号。

4.2. 运行容器

构建 Dockerfile 后,让我们运行容器:

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

*我们使用v选项运行容器,以便我们可以将当前目录映射到容器内的*home 目录。这样,我们可以提供接受命令的stdout 输出的输出文件。换句话说,我们已将命令输出重定向到home 目录中的output.txt文件。

运行容器时我们不会得到任何输出。但是,我们可以看到现在目录中有一个output.txt

让我们看一下文件的内容:

$ cat output.txt 
3.15.0

5. 使用shell表单

我们不必编写 bash 脚本来重定向输出。相反,我们可以使用命令中的*>特殊字符以shell*形式重定向 Docker中的命令。

5.1. 更改 Dockerfile

让我们重写 Dockerfile,使其具有以下内容:

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

我们使用shell形式来执行将输出重定向到文件的命令。

5.2. 运行容器

现在,我们可以构建 Dockerfile 并使用v选项运行容器:

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

我们得到相同的结果:

$ cat output.txt 
3.15.0

6. 在exec表单中传入Shell

另一种选择是以exec形式使用 shell。我们也可以使用这种方式选择我们想要的外壳。

6.1. 更改 Dockerfile

要以exec形式使用 shell ,我们可以这样编写 Dockerfile:

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

它使用 shell 作为主要命令来执行我们想要的另一个命令。此外,shell 命令的c选项从参数字符串中读取命令,而不是从标准输入中读取。

或者,我们可以使用不同的 shell,例如:

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

我们应该注意,命令不能分开

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

如果我们将命令分开,调用将如下所示:

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

shell 只读取cat作为命令字符串并忽略剩余的参数。因此,它与此命令调用相同:

$ /bin/sh -c cat

正确的命令调用是这样的:

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

因此,我们不应该分隔命令字符串。

6.2. 运行容器

现在,我们可以构建 Dockerfile 并使用v选项运行容器:

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

我们得到相同的结果:

$ cat output.txt 
3.15.0

7. 重定向stdoutstderr

容器中的命令输出**可以在stdoutstderr **中。我们可以将stdout输出和stderr输出重定向到两个单独的文件中。

为了演示这一点,我们将使用cat命令显示两个文件的内容。第一个文件是 /etc/alpine-release,第二个是一个不存在的文件,名为  file_does_not_exist.txt

catstdout中显示第一个文件的内容,对于第二个文件,它在stderr中显示错误。

让我们重写 Dockerfile:

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

当*> /home/output.txt部分将stdout输出重定向到一个文件时,2>/home/error.txt部分将stderr*输出重定向到另一个文件。

让我们构建 Dockerfile 并运行容器:

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

现在,我们得到了output.txt文件和error.txt文件:

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

8. 过滤输出

默认情况下,我们从容器中获取stdout输出和stderr输出。但是,我们可以选择只听stdoutstderr只有在不需要同时听的情况下。

让我们编写具有以下内容的 Dockerfile:

FROM alpine:latest
CMD cat /etc/alpine-release file_does_not_exist.txt

接下来,我们构建 Dockerfile 并运行容器:

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

但是,如果我们只想获取stderr输出而忽略stdout输出怎么办? 我们可以使用a选项来做到这一点:

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

** a 选项用于附加到标准输入、标准输出或标准错误。**