Jak mogę sprawdzić, czy jestem w podpowłoce?


24

Próbuję napisać funkcję, która zastąpi funkcjonalność exitwbudowanego systemu, aby uniemożliwić mi wyjście z terminala.

Próbowałem użyć SHLVLzmiennej środowiskowej, ale wydaje się, że nie zmienia się w podpowłokach:

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

Moja funkcja jest następująca:

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

Nie pozwoli mi to jednak korzystać z exitpodpowłok:

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

Jaka jest dobra metoda wykrywania, czy jestem w podpowłoce?



1
To z tego powodu . $ SHLVL jest 1, ponieważ jesteś nadal w poziomie powłoki 1, mimo że komenda echo $ SHLVL jest uruchamiany w „podpowłoce”. Zgodnie z tym postem podpowłoki spawnowane z nawiasami (...)dziedziczą wszystkie właściwości procesu nadrzędnego. Podane odpowiedzi są bardziej niezawodnymi rozwiązaniami do określania poziomu powłoki.
kemotep


5
@mosvy Czuję, że to inne pytanie. np. BASH_SUBSHELLodpowiedź (nawet jeśli jest kontrowersyjna) nie dotyczyłaby tego pytania.
Sparhawk

2
Widziałem tytuł na HNQ i myślałem, że to pytanie z mechaniki kwantowej ...
Mehrdad

Odpowiedzi:


43

W bash możesz porównać $BASHPIDdo$$

$ ( 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

Jeśli nie jesteś bash, $$powinieneś pozostać taki sam w podpowłoce, więc potrzebujesz innego sposobu uzyskania faktycznego identyfikatora procesu.

Jednym ze sposobów na uzyskanie faktycznego pid jest sh -c 'echo $PPID'. Jeśli po prostu umieścisz to na prostym ( … )ekranie, może się wydawać, że nie działa, ponieważ twoja powłoka zoptymalizowała rozwidlenie. Wypróbuj dodatkowe polecenia no-op, ( : ; sh -c 'echo $PPID'; : )aby przekonać się, że podpowłoka jest zbyt skomplikowana, aby ją zoptymalizować. Podziękowania dla John1024 za przepełnienie stosu za to podejście.


Może chcesz to zmienić, aby  (sh -c 'echo $PPID'; : )- patrz mój komentarz na odpowiedź John1024 użytkownika .
G-Man mówi „Reinstate Monica”

@ G-Man Cóż, to było po prostu przetestowanie (ponieważ w rzeczywistości byłoby to bardziej skomplikowane) ... ale tak, byłoby najlepiej, gdyby test działał we wszystkich powłokach. Więc umieściłem zakaz zarówno przed, jak i po, który, mam nadzieję, poradzi sobie ze wszystkim.
derobert

38

Jak o BASH_SUBSHELL?

BASH_SUBSHELL
      Zwiększany o jeden w ramach każdej podpowłoki lub środowiska podpowłoki, gdy powłoka
      zaczyna działać w tym środowisku. Wartość początkowa to 0.

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

16
Byłoby to wygodne polecenie w filmie „Początek”.
Eric Duminil

Na początku jest to prawdopodobnie $ SHLVL
Babcia

19

[powinien to być komentarz, ale moje komentarze są zwykle usuwane przez moderatorów, więc pozostanie to odpowiedzią, że mógłbym użyć go jako odniesienia, nawet jeśli zostanie usunięty]

Używanie BASH_SUBSHELLjest całkowicie zawodne, ponieważ może być ustawione tylko na 1 w niektórych podpowłokach, a nie we wszystkich podpowłokach.

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

Przed twierdzeniem, że podproces, w którym uruchamiane jest polecenie potoku, nie jest naprawdę prawdziwą podpowłoką, rozważ ten man bashfragment:

Każde polecenie w potoku jest wykonywane jako osobny proces (tj. W podpowłoce).

i praktyczne implikacje - to, czy fragment skryptu jest uruchamiany podproces, czy nie, co jest niezbędne, a nie jakaś sprzeczność terminologiczna.

Jedynym rozwiązaniem, jak już wyjaśniono w odpowiedziach na to pytanie, jest sprawdzenie, czy $BASHPIDjest równe, $$czy przenośne, ale znacznie mniej wydajne:

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

11
Nit: BASH_SUBSHELLjest ustawiony dość niezawodnie, ale prawidłowe uzyskanie jego wartości jest niepewne. Zwróć uwagę na to, co mówią dokumenty : „Zwiększone o jeden w obrębie każdej podpowłoki lub środowiska podpowłoki, gdy powłoka zaczyna wykonywać się w tym środowisku. ” Myślę, że w przykładzie potoku bash nie zaczął jeszcze działać w tej podpowłoce, gdy zmienna jest rozwijana. Możesz porównać echo $BASH_VERSIONz declare -p BASH_VERSION- ten ostatni powinien niezawodnie wyprowadzać 1 z
potokami

6
Powiedzmy nawet, eval 'echo $BASH_SUBSHELL $BASHPID' | catże wypisze 1 dla BASH_SUBSHELL, ponieważ zmienna jest rozwijana po rozpoczęciu wykonywania.
muru

4
wszystkie te argumenty powinny również dotyczyć podstawiania procesów i poleceń, procesów bg, ale tylko potoki są różne. Patrząc na kod, inkrementacja subshell_levelnaprawdę jest odroczona w przypadku rurociągów pierwszego planu , co prawdopodobnie ma jakiś powód, ale którego nie jestem w stanie zrozumieć ;-)
mosvy

2
Masz rację. Wygląda na to, że Chet wyraźnie tego zamierza. lists.gnu.org/archive/html/bug-bash/2015-06/msg00050.html : „BASH_SUBSHELL mierzy (...) podpowłoki, a nie elementy potoku.” lists.gnu.org/archive/html/bug-bash/2015-06/msg00054.html : „Zastanowię się, czy powinienem udokumentować status quo, czy rozwinąć definicję„ podpowłoki ”, którą odzwierciedla $ BASH_SUBSHELL. „
muru

2
@JoL się mylisz, rozszerzenie odbywa się również w osobnym procesie, przeczytaj linki i przykłady z powyższej dyskusji; lub po prostu spróbuj echo $$ $BASHPID $BASH_SUBSHELL | cat.
mosvy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.