我正在尝试使用环境变量在 Docker 容器内运行命令,但我找不到正确的语法。
我可以看到变量设置正确:
$ docker exec -e FOO=bar cfdb72_db_1 env | grep FOO
FOO=bar
我以为双美元符号($$
)在这里可以起作用,但是这会打印出一个奇怪的数字。
$ docker exec -e FOO=bar cfdb72_db_1 echo $FOO
$ docker exec -e FOO=bar cfdb72_db_1 echo '$FOO'
$FOO
$ docker exec -e FOO=bar cfdb72_db_1 echo \$FOO
$FOO
$ docker exec -e FOO=bar cfdb72_db_1 echo $$FOO
24136FOO
答案1
事实
docker exec
运行可执行文件,而不是 shell 中的命令。- 在您的情况下,可执行文件是
echo
。 echo
不关心环境变量。当它获取到时$FOO
,它会打印$FOO
。
您的尝试解释
-
docker exec -e FOO=bar cfdb72_db_1 echo $FOO
docker
是一个命令。exec
、-e
、FOO=bar
、cfdb72_db_1
和echo
是命令将获取的命令行参数。$FOO
不带引号,因此当前 shell 会将其展开。 当前 shell 中不存在该变量(或者可能存在但包含所有空格或空字符串)。"$FOO"
将使 shell 在 之后传递一个附加参数echo
。 不带引号$FOO
可以扩展为零个或多个单词;在您的特定情况下,该数字为零。因此,就像您运行:docker exec -e FOO=bar cfdb72_db_1 echo
在容器中运行的命令是 sole
echo
。 Soleecho
只打印一个换行符。 -
docker exec -e FOO=bar cfdb72_db_1 echo '$FOO'
$FOO
是单引号,那么 shell 不会将其视为要扩展的变量。 shell 会在将文字$FOO
字符串作为参数传递给之前删除引号docker
。docker
可执行文件接收exec
、-e
、FOO=bar
、cfdb72_db_1
、echo
和$FOO
作为参数。在容器中运行的命令类似于echo $FOO
,但是没有要扩展 的 shell$FOO
。事实上我不应该写echo $FOO
因为这个字符串不存在作为必须拆分的单个实体。您的 shell 已经识别了原始命令中的单词,它将echo
和$FOO
作为单独的参数传递给docker
,从那时起,它们被视为某个数组的不同成员。将要在容器中运行的命令视为echo
$FOO
。echo $FOO
在容器中echo
直接运行并接收$FOO
字面意思,因此它会打印$FOO
(后跟换行符,因为这是echo
默认的工作方式)。 -
docker exec -e FOO=bar cfdb72_db_1 echo \$FOO
$
被转义,shell 不会将$FOO
in视为要扩展的变量。shell 所做的唯一一件事是在将文字字符串作为参数\$FOO
传递给 之前删除反斜杠。其余的发生与上面完全相同。$FOO
docker
-
docker exec -e FOO=bar cfdb72_db_1 echo $$FOO
$$
不带引号,当前 shell 会将其展开。它是一个特殊参数,会展开为 shell 的十进制进程 ID。显然24136
在您的示例中是这样的。展开后$$FOO
变为24136FOO
。在容器中运行的命令类似于echo 24136FOO
(再次考虑echo
24136FOO
而不是echo 24136FOO
)。实际上echo
会打印24136FOO
(和换行符)。
解决方案
如果想要$FOO
扩展,你需要一个可以扩展它的 shell:
docker exec -e FOO=bar cfdb72_db_1 sh -c 'echo "$FOO"'
现在在容器中运行的命令带有sh
参数-c
和echo "$FOO"
。
笔记:
$FOO
是正确双引号当容器内的新 shell 解释代码时。有两个 shell,两层引用。单引号用于当前 shell;双引号用于新 shell。- 在大多数 shell 中(以及在大多数 实现中
sh
)echo
都是内置命令。在所有示例中echo
都是独立的可执行文件(/bin/echo
或容器内的任何内容)。两者应该(大部分)等效。但一般来说,请记住,shell 中具有某个名称的命令和直接调用的具有相同名称的命令不一定是同一件事。
答案2
您正在容器中执行 echo 命令。echo 命令不会扩展$
,这是 shell 的工作。您还在 shell 中运行 docker 命令,如果您不从该 shell 中转义变量,该命令将扩展变量,因此 docker 甚至看不到$
,更不用说容器内的进程了。
要运行 shell 并避免$FOO
在 shell 中扩展解析命令docker
,您可以/bin/sh
在容器内使用命令行上的单引号:
docker exec -e FOO=bar cfdb72_db_1 /bin/sh -c 'echo "$FOO"'