проблема на уровне оболочки в Ubuntu 20.04

проблема на уровне оболочки в Ubuntu 20.04

следующая команда вернула значение 1 для Ubuntu 20.04, но 0 для Ubuntu 18.04,

$ ssh user@remote bash -exl -c 'exit 0'

Основная причина в том, что у нас разные значения $SHLVL при запуске команды в разных версиях Ubuntu. Так является ли это ожидаемым поведением?

+ '[' '' ']'
+ '[' -d /etc/profile.d ']'
+ for i in /etc/profile.d/*.sh
+ '[' -r /etc/profile.d/01-locale-fix.sh ']'
+ . /etc/profile.d/01-locale-fix.sh
+++ /usr/bin/locale-check C.UTF-8
++ eval
+ for i in /etc/profile.d/*.sh
+ '[' -r /etc/profile.d/apps-bin-path.sh ']'
+ . /etc/profile.d/apps-bin-path.sh
++ snap_bin_path=/snap/bin
++ '[' -n /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games -a -n /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games ']'
++ export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
++ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
++ '[' -z '' ']'
++ export XDG_DATA_DIRS=/usr/local/share:/usr/share
++ XDG_DATA_DIRS=/usr/local/share:/usr/share
++ snap_xdg_path=/var/lib/snapd/desktop
++ '[' -n /usr/local/share:/usr/share -a -n /usr/local/share:/usr/share ']'
++ export XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
++ XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
+ for i in /etc/profile.d/*.sh
+ '[' -r /etc/profile.d/bash_completion.sh ']'
+ . /etc/profile.d/bash_completion.sh
++ '[' 'x5.0.17(1)-release' '!=' x -a x '!=' x -a x = x ']'
+ for i in /etc/profile.d/*.sh
+ '[' -r /etc/profile.d/cedilla-portuguese.sh ']'
+ . /etc/profile.d/cedilla-portuguese.sh
++ '[' en = pt -a en '!=' pt ']'
+ for i in /etc/profile.d/*.sh
+ '[' -r /etc/profile.d/gawk.sh ']'
+ . /etc/profile.d/gawk.sh
+ for i in /etc/profile.d/*.sh
+ '[' -r /etc/profile.d/Z97-byobu.sh ']'
+ . /etc/profile.d/Z97-byobu.sh
++ '[' -r /usr/bin/byobu-launch ']'
++ '[' '' = 0 ']'
++ '[' '' = 1 ']'
++ '[' -e /etc/byobu/autolaunch ']'
++ '[' '' = byobu ']'
++ '[' '' = byobu-screen ']'
++ '[' '' = byobu-tmux ']'
+ unset i
+ '[' -n '5.0.17(1)-release' ']'
+ '[' -f /home/jenkins/.bashrc ']'
+ . /home/jenkins/.bashrc
++ case $- in
++ return
+ '[' -d /home/jenkins/bin ']'
+ PATH=/home/jenkins/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
+ exit
++ '[' 1 = 1 ']'
++ '[' -x /usr/bin/clear_console ']'
++ /usr/bin/clear_console -q

и вывод той же команды, запущенной на Ubuntu 18.04,

+ '[' '' ']'
+ '[' -d /etc/profile.d ']'
+ for i in /etc/profile.d/*.sh
+ '[' -r /etc/profile.d/01-locale-fix.sh ']'
+ . /etc/profile.d/01-locale-fix.sh
+++ /usr/bin/locale-check C.UTF-8
++ eval
+ for i in /etc/profile.d/*.sh
+ '[' -r /etc/profile.d/apps-bin-path.sh ']'
+ . /etc/profile.d/apps-bin-path.sh
++ snap_bin_path=/snap/bin
++ '[' -n /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games -a -n /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games ']'
++ export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
++ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
++ '[' -z '' ']'
++ export XDG_DATA_DIRS=/usr/local/share:/usr/share
++ XDG_DATA_DIRS=/usr/local/share:/usr/share
++ snap_xdg_path=/var/lib/snapd/desktop
++ '[' -n /usr/local/share:/usr/share -a -n /usr/local/share:/usr/share ']'
++ export XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
++ XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
+ for i in /etc/profile.d/*.sh
+ '[' -r /etc/profile.d/bash_completion.sh ']'
+ . /etc/profile.d/bash_completion.sh
++ '[' -n '4.4.20(1)-release' -a -n '' -a -z '' ']'
+ for i in /etc/profile.d/*.sh
+ '[' -r /etc/profile.d/cedilla-portuguese.sh ']'
+ . /etc/profile.d/cedilla-portuguese.sh
++ '[' en = pt -a en '!=' pt ']'
+ for i in /etc/profile.d/*.sh
+ '[' -r /etc/profile.d/Z97-byobu.sh ']'
+ . /etc/profile.d/Z97-byobu.sh
++ '[' -r /usr/bin/byobu-launch ']'
++ '[' '' = 0 ']'
++ '[' '' = 1 ']'
++ '[' -e /etc/byobu/autolaunch ']'
++ '[' '' = byobu ']'
++ '[' '' = byobu-screen ']'
++ '[' '' = byobu-tmux ']'
+ unset i
+ '[' -n '4.4.20(1)-release' ']'
+ '[' -f /home/jenkins/.bashrc ']'
+ . /home/jenkins/.bashrc
++ case $- in
++ return
+ '[' -d /home/jenkins/bin ']'
+ PATH=/home/jenkins/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
+ exit
++ '[' 2 = 1 ']'

решение1

Оглядываясь вокруг, это обсуждалось в некоторой степени вbug-bashсписки рассылки, гдеСтефан Шазеласхотя это было бы из-заисправление в поведенииSHLVLкогда bash выполняет оптимизацию хвостового вызова.

По сути, когда вы делаете bash -c 'simple-command', bash обычно выполняет оптимизацию: вместо того, чтобы forking и затем execing simple-command, он напрямую execвыполняет простую команду. Это обычная оптимизация, другие оболочки, такие как zsh, также делают то же самое. Раньше, в некоторых случаях это изменялось неправильно, и было внесено изменение, чтобы исправить это, что сломало это конкретное использование.

Например, в bash 4.4-19 версии 18.04 можно увидеть SHLVLувеличение get, даже если новый процесс не был запущен:

# env SHLVL=0 strace -fe execve bash -c 'bash -c "declare -p SHLVL"'
execve("/bin/bash", ["bash", "-c", "bash -c \"declare -p SHLVL\""], 0x7ffd6525c6f0 /* 8 vars */) = 0
execve("/bin/bash", ["bash", "-c", "declare -p SHLVL"], 0x562fd3b437e0 /* 8 vars */) = 0
declare -x SHLVL="2"

Это было бы правильным поведением при форке, например, когда эта оптимизация не выполнена:

# env SHLVL=0 strace -fe execve bash -c ':; bash -c "declare -p SHLVL"'
execve("/bin/bash", ["bash", "-c", ":; bash -c \"declare -p SHLVL\""], 0x7ffea8bb7c30 /* 8 vars */) = 0
strace: Process 284 attached
[pid   284] execve("/bin/bash", ["bash", "-c", "declare -p SHLVL"], 0x55fe735077e0 /* 8 vars */) = 0
declare -x SHLVL="2"
[pid   284] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=284, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

Это исправлено в bash 5.0, где:

% env SHLVL=0 strace -fe execve bash -c 'bash -c "declare -p SHLVL"'
execve("/usr/bin/bash", ["bash", "-c", "bash -c \"declare -p SHLVL\""], 0x7ffc70b6cd20 /* 59 vars */) = 0
execve("/usr/bin/bash", ["bash", "-c", "declare -p SHLVL"], 0x55f22a0343a0 /* 59 vars */) = 0
declare -x SHLVL="1"
+++ exited with 0 +++

По моему мнению, это не так, в проверке в , .bash_logoutкоторая, по-моему, недостаточно точна. Простой проверки SHLVLнедостаточно, если он собирается очистить терминал, он должен также проверить, запущен ли он на самом деле в терминале. Что-то вроде [ -t 0 ]:

  -t FD          True if FD is opened on a terminal.

По моему скромному мнению, это ошибка Ubuntu (точнее, Debian), .bash_logoutи о ней следует сообщить как о таковой. SHLVLОна не является стандартной, поэтому я не думаю, что она достаточно хороша, чтобы быть единственным тестом, когда оболочки могут выполнять оптимизации, которые могут повлиять на ее ценность.

Связанный контент