让我们看下面的例子:
[user@user~]$ sudo docker run -d -e XYZ=123 ubuntu sleep 10000
2543e7235fa9
[user@user~]$ sudo docker exec 2543e7235fa9 echo test
test
[user@user~]$ sudo docker exec 2543e7235fa9 echo $XYZ
<empty row>
[user@user~]$ sudo docker exec -it 2543e7235fa9 bash
root@2543e7235fa9:/# echo $XYZ
123
为什么我得到<empty row>
而不是123
?为什么执行并进入 bash 后我能够看到XYZ=123
?
答案1
您在这里缺少的两件事是:
docker exec ... <command>
默认情况下不在<command>
shell 中运行,它只是<command>
在没有 shell 的容器中运行。如果您想在非交互式 shell 中运行 docker 中的命令,请使用:
docker exec <container> bash -c '<command>'
如果
<command>
有多个单词,则需要用单引号或双引号将整个命令传递给bash -c
.例如
sudo docker exec 2543e7235fa9 bash -c 'echo $XYZ'
当你这样做时,有两个 shell 很重要:
- 您正在运行的 shell
sudo docker exec ...
(称为“Shell A”) - 在容器内运行的 shell(称为“Shell B”)。
如果你不在 shell A 中反斜杠转义或单引号,
$
然后 shell A 将插入它自己的值$XYZ
(即使它没有值,也会返回一个空字符串)。因此,如果
XYZ=5
在 Shell A 中,那么您的sudo docker exec 2543e7235fa9 echo $XYZ
与相同exec 2543e7235fa9 echo 5
(并且不会有 shell B 因为您没有告诉 docker to runbash -c ...
)。如果你做转义或单引号
$
,然后将其按原样传递给 shell B,并且 shell B 为 插入其值$XYZ
。- 您正在运行的 shell
换句话说,使用:
sudo docker exec 2543e7235fa9 bash -c 'echo $XYZ'
或者
sudo docker exec 2543e7235fa9 bash -c "echo \$XYZ"
在我看来,单引号形式更容易理解它在做什么以及大多数时候你应该使用什么。单引号内不会发生变量插值,因此命令的传递方式与 shell B 完全相同。
当您需要将变量从 shell A 传递到 shell B 时,双引号形式非常有用。如果您还需要传递文字$
,则应使用反斜杠转义。例如,让 shell B 回显 shell A$ABC
和它自己的$XYZ
:
sudo docker exec 2543e7235fa9 bash -c "echo $ABC \$XYZ"
如果 shell A$ABC
等于123 10
,shell B$XYZ
等于 123,那么将输出:
10 123
注意:除非$XYZ
在 shell B 的启动文件之一中定义,或者docker run -e XYZ=123
如您在示例中使用的那样(或使用-env-file
),否则它将没有值。
答案2
因为$XYZ
它会被运行命令的 shell 扩展docker exec
。您需要引用它才能将$XYZ
字符串放入其中。
sudo docker exec ... echo \$XYZ
确实存在一个误解 - 什么是参数以及它们在 shell 中的行为方式。当你跑步时
any-command $XYZ
从来any-command
没有见过$XYZ
。您的 shell 会将其替换为该参数(在该 shell 中)具有的任何值。由于您没有指定参数XYZ
或者参数为空,因此您提交到 shell 的实际命令行是
sudo docker exec 2543e7235fa9 echo