Почему `bash -c somecommand` иногда не выходит из процесса bash?

Почему `bash -c somecommand` иногда не выходит из процесса bash?

На Ubuntu 12.04 с GNU bash версии 4.2.25(1)-release (x86_64-pc-linux-gnu) я попробовал следующую команду:

$ bash -c 'pstree -s $$'
init───sshd───sshd───sshd───bash───pstree
$ bash -c 'pstree -s $$;echo'
init───sshd───sshd───sshd───bash───bash───pstree

Думаю, второй вариант соответствует моим ожиданиям: в первом bashслучае я выполнил эти команды; во втором bash— то, с чего я начал bash -c ...; а затем второй bash запустит подпроцесс с именем pstree.

Однако мне интересно, что случилось с первым. Почему второй bashисчез и pstreeстал подпроцессом оригинала bash? И почему ответ на предыдущий вопрос не применим ко второму bash -c ...?

решение1

Я бы сказал, что это простоХвост Вызов Оптимизация, но на самом деле (как указывает последняя ссылка) bashне оптимизирует хвостовые вызовы. Кажется, что он оптимизирует только случай, когда команда, которая должна быть выполнена, является простой командой (то есть не составной командой), хотя не тогда, когда есть перенаправления. Кажется, он также оптимизирует, когда команда является последней в списке and-or (как в true && true && cmdor false && true || cmd), но не тогда, когда есть traps на месте (как в trap uname EXIT && cmd), поскольку это было бы неправильно.

Вторая команда не является хвостовым вызовом pstree, поскольку pstreeне является последней в командной строке. (Это хвостовой вызов echo, но echoобычно он встроен, поэтому для него не будет создан подпроцесс.)

Чтобы не читать все эти ссылки (хотя я надеюсь, что они будут интересными), идея в том, что если вы знаете, что функция/программа/что угодно вернет управление сразу после вызова какой-то другой функции/программы/чего угодно, и что возвращаемое значение будет значением, возвращаемым вызванной функцией/программой/чем угодно, то вы можете просто повторно использовать текущий стековый фрейм (или процесс, в случае скрипта оболочки) вместо того, чтобы помещать новый стековый фрейм (создавать новый процесс), вызывать функцию (запускать скрипт) и затем возвращаться. В скрипте оболочки вы можете сделать это вручную, используя execдля последней команды, но оболочка могла бы сделать это автоматически.

zshи kshоба, похоже, способны это сделать, но не bash:

$ zsh -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───pstree
$ ksh -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───pstree
$ bash -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───bash───pstree

Но это всего лишь наблюдение, основанное на версиях тех оболочек, которые у меня случайно установлены, так что YMMV:

$ zsh --version
zsh 5.0.2 (x86_64-pc-linux-gnu)
$ ksh --version
  version         sh (AT&T Research) 93u+ 2012-08-01
$ bash --version
GNU bash, version 4.2.45(1)-release (x86_64-pc-linux-gnu)

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