为了展示我正在尝试做的事情,这是我到目前为止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""'
所以让我们分解一下长命令:
我将进入一个主机,在其中运行bash并使用docker ps
提取正确的容器,然后执行docker exec
在容器中运行外壳程序以加载我的{{1 }}需要工作。需要注意的重要一点是,$COMMAND
应该是容器内$BAR
变量的值。
这就是我想要在理论上完成的事情。但是,无论我如何设置大括号,引号或转义字符,运行此命令时-我总是遇到问题,要么Shell语法不正确,要么它运行的命令不正确(特别是当命令具有多个参数时)或加载BAR
值来自我的本地桌面或远程主机,但不来自容器。
使用单壳单缸衬套甚至有可能吗?
答案 0 :(得分:2)
我认为我们可以简化您的命令。
首先,这里不需要使用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
运行它。有一对
解决方案。我们可以确保保护一切
命令行中针对外壳扩展的额外级别,例如
这个:
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
这简化了获取商品所需的报价和转义 传递到容器外壳。
答案 1 :(得分:0)
感谢larsks的出色解释,我知道它能正常工作,我的最后一句话是:
ssh -i $ECS_SSH_KEY ec2-user@$EC2_IP -t "bash -c \"docker exec -it \$( docker ps -a -q -f name=$CONTAINER_DOCKER_NAME | head -n 1 ) /bin/sh -c \\\"eval \\\\\\\$(AWS_ENV_PATH=/\\\\\\\$ENVIRONMENT /bin/aws-env) && $COMMAND\\\"\""
因此,基本上,您将所有内容都用双引号引起来,然后在其中也使用双引号,因为我们需要宿主提供的一些变量,例如$DOCKER_CONTAINER_NAME
。转义使用\的引号和$符号。
但是因为我们有多个级别的外壳程序(主机,服务器,容器),所以我们还需要使用多个级别的转义。因此第一级只是\ $,它将保护该变量(或诸如docker ps
之类的shell命令)不在主机上而是在服务器上运行。
那么下一个转义级别是7倍。每个\都会使字符向右转义,因此最后在第二级(服务器)上是\\\$
,在第三级(容器)上是\$
。这样可以确保在服务器上而不是容器中评估变量。
双引号的原理相同。 \"
之间的所有内容都在第二级上运行,\\\"
之间的所有内容都在第三级上运行。