Этот ответ, возможно, все еще неполон, но очарован проблемой, я потратил некоторое время на его отладку, используя следующий 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 не выполняется в соответствующих точках (т. е. переменная правильно отслеживает фактическое вложение оболочки во всех случаях, просто иногда бывает меньше работающих оболочек, чем ожидалось).