Dockerfile中Shell特殊变量$_的解析异常问题咨询
问题原因与解析逻辑
核心原因:Shell变量展开时机与默认Shell的行为
你的问题出在Docker RUN指令的执行环境和Shell变量$_的展开时机上:
- Docker默认使用
/bin/sh(Ubuntu镜像中是dash,一种POSIX兼容的轻量Shell)执行RUN的shell形式指令,且会将你的整个RUN命令包裹为/bin/sh -c "mkdir sample_dir && cd $_ && touch sample_file"执行。 - 在dash的
sh -c "命令串"环境中,所有变量会在整个命令串执行前一次性展开,而不是分步执行时动态更新。此时$_在Shell启动时是unset状态,cd $_等价于不带参数的cd命令——而不带参数的cd会默认切换到当前用户的主目录(即/root),最终导致sample_file被创建在/root下。
$_的解析逻辑
在POSIX兼容Shell(如dash)中,$_的行为分两种场景:
- 交互式Shell:每执行完一个命令,
$_会被更新为该命令的最后一个参数。比如你在终端手动输入mkdir sample_dir && cd $_,cd会正确切换到sample_dir,因为第一个命令执行后$_已经被设置为sample_dir,第二个命令使用的是更新后的值。 - 非交互式Shell(如
sh -c):启动时$_初始值取决于启动方式:- 如果是执行脚本文件,
$_是脚本的路径; - 如果是
sh -c "命令串"且无额外参数,$_初始为unset; - 命令执行后
$_会被更新,但这个更新仅对后续独立执行的命令生效——而在同一个sh -c包裹的命令串中,变量展开是提前完成的,所以后续命令无法获取上一个命令执行后更新的$_值。
- 如果是执行脚本文件,
为什么SHELL指令无效?
你添加的SHELL ["/bin/bash"]放在了RUN指令之后,而SHELL指令仅会影响后续的RUN、CMD、ENTRYPOINT等指令,对之前的RUN不起作用。如果把SHELL指令移到RUN之前,使用bash执行该命令,结果会不同——bash在bash -c "命令串"中会按顺序执行命令并动态更新$_,此时cd $_会正确切换到sample_dir。
验证依据
从你的docker history输出可以看到,该RUN指令的执行命令是:
RUN /bin/sh -c mkdir sample_dir && cd $_ && touch sample_file # buildkit
明确使用了/bin/sh执行,符合上述的unset$_导致cd到主目录的逻辑。
内容的提问来源于stack exchange,提问作者knuff22
相关产品推荐
相关产品推荐

