【问题标题】:Bash instances are not nesting in Dockerfile `RUN`Bash 实例未嵌套在 Dockerfile `RUN` 中
【发布时间】:2019-10-31 12:29:03
【问题描述】:

嵌套的 bash 实例递增 SHLVL 环境变量 (http://tldp.org/LDP/abs/html/internalvariables.html)。

例如:

$ echo $SHLVL
1
$ bash -c "echo \$SHLVL"
2
$ bash -c "bash -c 'echo \$SHLVL'"
3

查看https://github.com/dgroomes/docker-shell-example/commit/60091eb63545f24a9fb3ccf38649ea5098bdbd0d/checks?check_suite_id=283012138#step:5:2

但是当我在 Dockerfile 中尝试相同的操作时,SHLVL 保持在 1。为什么?

请参阅https://github.com/dgroomes/docker-shell-example/blob/60091eb63545f24a9fb3ccf38649ea5098bdbd0d/Dockerfile#L5 上的最小可重现示例。为了更多的证据,这个 Dockerfile 在 CI 工具 Github Actions 中的执行https://github.com/dgroomes/docker-shell-example/commit/60091eb63545f24a9fb3ccf38649ea5098bdbd0d/checks?check_suite_id=283012138#step:8:15

这里的问题浓缩为 4 行(它应该打印 2 然后 0):

Step 2/2 : RUN bash -c "bash -c 'echo \$SHLVL; echo \$BASH_SUBSHELL'"
 ---> Running in ce2362419426
1
0

【问题讨论】:

  • 尝试使用 '$BASH_SUBSHELL' 看看是否有帮助
  • 感谢@EyalGolan 的建议,我试过了 github.com/dgroomes/docker-shell-example/commit/…> 这部分正在按我的预期工作,其中BASH_SUBSHELL 仅在子shell 中递增,但在嵌套shell 中不递增(我没有不太了解 Bash 如何跟踪这种差异,但在示例中进行了解释 tldp.org/LDP/abs/html/subshells.html#SUBSHELL>)
  • 我明白了。感谢您的详细解释:)
  • 有趣。您是否尝试将" 替换为'?即RUN ['bash', '-c', 'bash -c "echo SHLVL: \$SHLVL"']
  • 我怀疑 bash 在 dockerfile 运行的环境中实际上并不是 bash,假设 'bash' 只是 /bin/sh 的别名,那么 /bin/sh -c "/bin/ sh -c 'echo \$SHLVL'" 是 1。你能不能试试看你能不能找出 'bash' 实际解析到的路径。

标签: bash docker dockerfile


【解决方案1】:

这个答案可能仍然不完整,但对这个问题很着迷,我花了一些时间使用以下 Dockerfile 对其进行调试:

FROM debian:10
SHELL ["/bin/sh", "-ec"]
RUN apt-get update > /dev/null; apt-get -y install psmisc > /dev/null

# First layer is good: expected 1, got 1
RUN /bin/bash -exc "pstree; echo SHLVL=\$SHLVL"

# The surprising example (getting the escaping right is already tricky)
RUN /bin/bash -exc "/bin/bash -exc \"pstree; echo SHLVL=\\\$SHLVL\"" # expected 2, got 1
RUN /bin/bash -exc "/bin/bash -exc \"/bin/bash -exc \\\"pstree; echo SHLVL=\\\\\\\$SHLVL\\\"\"" # expected 3, got 1

# Now what happens if two commands run in the inner bash
RUN /bin/bash -exc ":; /bin/bash -exc \":; pstree; echo SHLVL=\\\$SHLVL\"" # expected 2, got 1
RUN /bin/bash -exc ":; /bin/bash -exc \":; /bin/bash -exc \\\":; pstree; echo SHLVL=\\\\\\\$SHLVL\\\"\"" # expected 3, got 1

有趣的事情似乎是:如果bash 调用之后紧跟着另一个,它将被“优化”(?)。据我所知,这不是 Docker 特有的东西,因为它可以交互地重现(在我的 Debian 10 系统上,问题中的交互式命令序列产生 1、2、2 而不是 1、2、3嵌套!)。

无论如何,构建 Dockerfile 的输出如下:

Sending build context to Docker daemon  30.21kB
Step 1/8 : FROM debian:10
 ---> 8e9f8546050d
Step 2/8 : SHELL ["/bin/sh", "-ec"]
 ---> Running in 3509ef249c45
Removing intermediate container 3509ef249c45
 ---> 8956c1fddb7c
Step 3/8 : RUN apt-get update > /dev/null; apt-get -y install psmisc > /dev/null
 ---> Running in 5cabec19144a
debconf: delaying package configuration, since apt-utils is not installed
Removing intermediate container 5cabec19144a
 ---> 64cad97f7793
Step 4/8 : RUN /bin/bash -exc "pstree; echo SHLVL=\$SHLVL"
 ---> Running in 22a0aa663163
+ pstree
sh---bash---pstree
+ echo SHLVL=1
SHLVL=1
Removing intermediate container 22a0aa663163
 ---> 4caa146b24f6
Step 5/8 : RUN /bin/bash -exc "/bin/bash -exc \"pstree; echo SHLVL=\\\$SHLVL\"" # expected 2, got 1
 ---> Running in 538ff45db230
+ /bin/bash -exc 'pstree; echo SHLVL=$SHLVL'
+ pstree
sh---bash---pstree
SHLVL=1
+ echo SHLVL=1
Removing intermediate container 538ff45db230
 ---> 1d4c9c2638fa
Step 6/8 : RUN /bin/bash -exc "/bin/bash -exc \"/bin/bash -exc \\\"pstree; echo SHLVL=\\\\\\\$SHLVL\\\"\"" # expected 3, got 1
 ---> Running in 3f0650d4d21b
+ /bin/bash -exc '/bin/bash -exc "pstree; echo SHLVL=\$SHLVL"'
+ /bin/bash -exc 'pstree; echo SHLVL=$SHLVL'
+ pstree
sh---bash---pstree
SHLVL=1
+ echo SHLVL=1
Removing intermediate container 3f0650d4d21b
 ---> 2d977033884d
Step 7/8 : RUN /bin/bash -exc ":; /bin/bash -exc \":; pstree; echo SHLVL=\\\$SHLVL\"" # expected 2, got 1
 ---> Running in 39b79af0f558
+ :
+ /bin/bash -exc ':; pstree; echo SHLVL=$SHLVL'
+ :
+ pstree
sh---bash---bash---pstree
+ echo SHLVL=2
SHLVL=2
Removing intermediate container 39b79af0f558
 ---> 48170e9bcb01
Step 8/8 : RUN /bin/bash -exc ":; /bin/bash -exc \":; /bin/bash -exc \\\":; pstree; echo SHLVL=\\\\\\\$SHLVL\\\"\"" # expected 3, got 1
 ---> Running in 456e6ec421ca
+ :
+ /bin/bash -exc ':; /bin/bash -exc ":; pstree; echo SHLVL=\$SHLVL"'
+ :
+ /bin/bash -exc ':; pstree; echo SHLVL=$SHLVL'
+ :
+ pstree
sh---bash---bash---bash---pstree
+ echo SHLVL=3
SHLVL=3
Removing intermediate container 456e6ec421ca
 ---> 30a07d3bdc95
Successfully built 30a07d3bdc95
Successfully tagged test:latest

最后,pstree 的输出很有趣,因为它显示了在各个点实际上没有其他 bash 进程在运行(即变量在所有情况下都正确跟踪实际的 shell 嵌套,只是有时存在运行的 shell 比预期的少)。

【讨论】:

    【解决方案2】:

    刚刚看了一下,我先观察以下,进入一个Dockerfile,然后输入类似'RUN gfddlgjkdlfgjk',它会出错并告诉你

    '/bin/sh -c gfddlgjkdlfgjk' returned a non-zero code

    由此我们推断出

    RUN bash -c "bash -c 'echo \$SHLVL'"

    实际执行

    /bin/sh -c "bash -c \"bash -c 'echo \$SHLVL'\""

    这确实是 Docker 之外的 1

    【讨论】:

      猜你喜欢
      • 2020-02-22
      • 1970-01-01
      • 2022-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-30
      相关资源
      最近更新 更多