Як я можу виявити, чи перебуваю в передплаті?


24

Я намагаюся написати функцію для заміни функціональності exitвбудованого, щоб не допустити виходу з терміналу.

Я спробував використовувати SHLVLзмінну середовища, але вона, схоже, не змінюється в межах підрозділів:

$ echo $SHLVL
1
$ ( echo $SHLVL )
1
$ bash -c 'echo $SHLVL'
2

Моя функція полягає в наступному:

exit () {
    if [[ $SHLVL -eq 1 ]]; then
        printf '%s\n' "Nice try!" >&2
    else
        command exit
    fi
}

Це не дозволить мені використовувати exitв межах підрозділів:

$ exit
Nice try!
$ (exit)
Nice try!

Який хороший метод виявити, перебуваю чи ні в підкашрі?



1
Це через це . $ SHLVL дорівнює 1, тому що ви все ще перебуваєте в рівні оболонки 1, хоча команда echo $ SHLVL виконується в "нижній частині". Згідно з цією посадою, допоміжні оболонки, породжені дужками, (...)успадковують усі властивості батьківського процесу. Надані відповіді - більш надійні рішення для визначення рівня вашої оболонки.
kemotep


5
@mosvy Я відчуваю, що це вже інше питання. наприклад, BASH_SUBSHELLвідповідь (навіть якщо спірна) не стосуватиметься цього питання.
Sparhawk

2
Побачив заголовок у HNQ і подумав, що це питання квантової механіки ...
Мехрдад

Відповіді:


43

У Баш, можна порівняти $BASHPIDз$$

$ ( if [ "$$" -eq "$BASHPID" ]; then echo not subshell; else echo subshell; fi )
subshell
$   if [ "$$" -eq "$BASHPID" ]; then echo not subshell; else echo subshell; fi
not subshell

Якщо ви не знаходитесь в bash, вам $$слід залишатись однаковим в підкашлі, тож вам знадобиться інший спосіб отримання фактичного ідентифікатора процесу.

Один із способів отримати фактичний під - це sh -c 'echo $PPID'. Якщо ви просто помістите це на звичайному рівні, ( … )воно може не працювати, оскільки ваша оболонка оптимізувала вилку. Спробуйте додаткові команди без використання, ( : ; sh -c 'echo $PPID'; : )щоб змусити їх думати, що підзарядка занадто складна для оптимізації. Кредит припадає на John1024 на переповнення стека для такого підходу.


Ви можете захотіти змінити що  (sh -c 'echo $PPID'; : )- см мій коментар на відповідь John1024 в .
G-Man каже: "Відновіть Моніку"

@ G-Man Ну, це було лише для того, щоб перевірити його (оскільки в реальному використанні це було б дещо складніше) ... але так, було б найкраще, якби тест працював у всіх оболонках. Тож я поставив неоперацію до і після, що, сподіваюся, впорається з усім.
дероберт

38

Як щодо BASH_SUBSHELL?

BASH_SUBSHELL Підвищується одиницею
      в кожному середовищі нижньої оболонки або допоміжної оболонки, коли оболонка
      починає виконувати в цьому середовищі. Початкове значення 0.

$ echo $BASH_SUBSHELL
0
$ (echo $BASH_SUBSHELL)
1

16
Це було б зручною командою у фільмі Inception.
Ерік Думініл

На початку це, мабуть, $ SHLVL
Бабуся

19

[це повинен був бути коментарем, але мої коментарі, як правило, видаляються модераторами, тому це залишатиметься відповіддю, що я можу використовувати його як посилання, навіть якщо видалено]

Використання BASH_SUBSHELLє абсолютно ненадійним, оскільки воно може бути встановлене лише 1 у деяких підкабелях, а не у всіх підрозділах.

$ (echo $BASH_SUBSHELL)
1
$ echo $BASH_SUBSHELL | cat
0

Перш ніж стверджувати, що підпроцес, який виконується командою конвеєра, не є дійсно реальною підскладом, врахуйте цей man bashфрагмент:

Кожна команда в конвеєрі виконується як окремий процес (тобто в підпакеті).

і практичні наслідки - це те, чи є фрагмент сценарію запущений підпроцесором чи ні, що є суттєвим, а не якась термінологічна підказка.

Єдине рішення, як уже було пояснено у відповідях на це питання, - це перевірити, чи $BASHPIDдорівнює, $$або портативно, але набагато менш ефективно:

if [ "$(exec sh -c 'echo "$PPID"')" != "$$" ]; then
    echo you\'re in a subshell
fi

11
Ніт: BASH_SUBSHELLвстановлюється досить надійно, але правильно визначити його значення - нелегко. Зверніть увагу на те, що кажуть документи : "Підвищується одиницею в кожній середовищі додаткової оболонки або допоміжної оболонки, коли оболонка починає виконуватись у цьому середовищі. " Я думаю, що в прикладі "pipe" "bash" ще не почав виконуватись у цій доподібній частині, коли змінна розширюється. Ви можете порівняти echo $BASH_VERSIONз declare -p BASH_VERSION- останній повинен надійно вихід 1 з трубами, фонових завдань і т.д.
Муру

6
Навіть скажімо, eval 'echo $BASH_SUBSHELL $BASHPID' | catбуде видано 1 для BASH_SUBSHELL, тому що змінна розширюється після запуску виконання.
муру

4
всі ці аргументи також повинні застосовуватися до процесів заміни процесів і команд, bg-процесів, але лише трубопроводи відрізняються. Дивлячись на код, прирощення subshell_levelдійсно відкладається у випадку з трубопроводами переднього плану , що, певно, має певні причини, але які я не в змозі розібрати ;-)
mosvy

2
Ти правий. Здається, Чет явно має намір це саме так. list.gnu.org/archive/html/bug-bash/2015-06/msg00050.html : "BASH_SUBSHELL вимірює (...) підшах, а не елементи конвеєра." списки . "
муру

2
@JoL Ви помиляєтеся, розширення відбувається і в окремому процесі, будь ласка, прочитайте посилання та приклади з цієї дискусії вище; або просто спробувати echo $$ $BASHPID $BASH_SUBSHELL | cat.
mosvy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.