如何在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. 重定向stdout和stderr
容器中的命令输出**可以在stdout或stderr **中。我们可以将stdout输出和stderr输出重定向到两个单独的文件中。
为了演示这一点,我们将使用cat命令显示两个文件的内容。第一个文件是 /etc/alpine-release,第二个是一个不存在的文件,名为 file_does_not_exist.txt。
cat在stdout中显示第一个文件的内容,对于第二个文件,它在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输出。但是,我们可以选择只听stdout或stderr只有在不需要同时听的情况下。
让我们编写具有以下内容的 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 选项用于附加到标准输入、标准输出或标准错误。**