首页 > 解决方案 > 从另一个命令获取一些环境变量后,在远程主机上运行的 docker 容器中运行任意命令

问题描述

为了展示我正在尝试做的事情,这是我目前拥有的 bash 脚本的一部分:

COMMAND="${@:1}"
CONTAINER_DOCKER_NAME=this-value-is-computed-prior
MY_IP=this-ip-is-computed-prior

ssh user@$MY_IP -t 'bash -c "docker exec -it $( docker ps -a -q -f name='$CONTAINER_DOCKER_NAME' | head -n 1 ) /bin/sh -c "eval $(echo export FOO=$BAR) && $COMMAND""'

因此,让我们分解一下长命令:
我正在 ssh 进入一个运行 bash 的主机,该 bash 使用它来获取正确的容器,docker ps然后我docker exec在容器中运行一个 shell 来加载一些我$COMMAND需要工作的环境变量。需要注意的是,$BAR应该是容器内BAR变量的值。 所以这就是我试图在理论上完成的事情。但是,无论我如何设置大括号、引号或转义字符,在运行它时 - 我总是遇到问题,要么是 shell 语法不正确,要么它没有运行正确的命令(特别是当命令有多个参数时)或者它加载来自我的本地桌面或远程主机的值,而不是来自容器的值。
$BAR

这甚至可以用一个单一的外壳单衬里吗?

标签: bashshelldockerssh

解决方案


我认为我们可以大大简化您的命令。

首先,这里不需要使用eval,也不需要&& 操作符:

/bin/sh -c "eval $(echo export FOO=$BAR) && $COMMAND"

反而:

/bin/sh -c "FOO=$BAR $COMMAND"

这将环境变量设置FOO$COMMAND.

接下来,您不需要这个复杂的docker ps表达式:

docker ps -a -q -f name="$CONTAINER_DOCKER_NAME"

Docker 容器名称是唯一的。如果你有一个容器名称存储在 中$CONTAINER_DOCKER_NAME,你可以运行:

docker exec -it $CONTAINER_DOCKER_NAME ...

这将docker命令简化为:

docker exec -it $CONTAINER_DOCKER_NAME \
  /bin/sh -c "FOO=\$BAR $COMMAND"

请注意我们是如何在其中转义的$$BAR因为我们希望在容器解释它,而不是通过我们当前的 shell。现在我们只需要安排通过ssh. 有几个解决方案。我们可以确保保护命令行上的所有内容免受额外级别的 shell 扩展,如下所示:

ssh user@$MY_IP "docker exec -it $CONTAINER_DOCKER_NAME \
  /bin/sh -c \"FOO=\\\$BAR $COMMAND\""

我们需要将整个命令用双引号括起来,这意味着我们需要对命令内的任何引号进行转义(我们不能使用单引号,因为我们实际上想在 $CONTAINER_DOCKER_NAME本地扩展变量)。我们将失去一层 \扩展,所以我们的\$BAR变为\\\$BAR.

如果您的命令不是交互式的,您可以通过将脚本传送到 bash 而不是将其包含在命令行中来减少麻烦,如下所示:

ssh user@$MY_IP docker exec -i $CONTAINER_DOCKER_NAME /bin/sh <<EOF
  FOO=\$BAR $COMMAND
EOF

这简化了将事物传递到容器外壳所需的引用和转义。


推荐阅读