在Makefile中执行docker run时如何转义美元符号访问Docker环境变量
我来帮你理清楚这个问题,核心是要搞懂三层变量解析的嵌套逻辑:Makefile自身的变量解析、宿主机shell对docker命令的解析、容器内部shell对bash命令的解析。咱们逐个拆解你的测试案例:
测试1:
echo $SRC
Makefile里的$是变量引用的前缀,它会优先解析$SRC。如果你的Makefile没定义SRC变量,这部分直接被替换成空字符串。最终传给docker的命令变成:docker run --rm -t --env SRC="/mypath" ubuntu:xenial /bin/bash -c "echo "容器里执行自然啥也输出不了。
测试2:
echo $$SRC
Makefile里的$$会被转义成单个$,所以解析后给到宿主机shell的命令是:docker run --rm -t --env SRC="/mypath" ubuntu:xenial /bin/bash -c "echo $SRC"这时候轮到宿主机shell解析
$SRC了,如果宿主机环境没定义这个变量,它又被替换成空,容器里还是执行echo,依然没输出。测试3:
echo $$$$SRC
这才是能得到正确结果的写法!Makefile里每一对$$会转成单个$,所以$$$$最终变成$$,解析后传给宿主机shell的命令是:docker run --rm -t --env SRC="/mypath" ubuntu:xenial /bin/bash -c "echo $$SRC"宿主机shell处理时,
$$又转成单个$,最终容器内的shell拿到的是echo $SRC,这时候容器里定义的SRC="/mypath"就会被正确解析,输出你想要的/mypath。
另外还有个更清晰的替代方案:用单引号包裹bash -c的命令内容,这样宿主机shell不会解析里面的$符号。在Makefile里这么写就行:
$(eval $(call create-test,4,echo '$SRC' ))
Makefile里单引号不需要转义,解析后宿主机shell拿到的是echo '$SRC',它不会处理单引号里的$SRC,直接原封不动传给容器内的shell,容器里就能正确解析环境变量了。
给你一个完整的可运行示例:
define create-test so-test-$1: docker run \ --rm \ -t \ --env SRC="/mypath" \ ubuntu:xenial \ /bin/bash -c "$2" endef # 写法1:四层美元符号转义 $(eval $(call create-test,3,echo $$$$SRC )) # 写法2:单引号包裹变量 $(eval $(call create-test,4,echo '$SRC' ))
内容的提问来源于stack exchange,提问作者Makers_F

