sposób na sprawdzenie bieżącej powłoki zgodny z checkbashisms

18

W moim .profile używam następującego kodu, aby upewnić się, że aliasy i funkcje związane z Bash są pozyskiwane tylko wtedy, gdy powłoką logowania jest Bash :

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

Obecnie jestem w trakcie kontroli plików konfiguracyjnych powłoki, skryptów i funkcji pod kontrolą wersji. Ja również niedawno rozpoczęła proces usuwania przypadkowych Bashisms ze skryptów powłoki, które nie korzystają z Bash-specyficzne cechy, np wymianie function funcname()z funcname().

Dla mojego repozytorium plików powłoki skonfigurowałem hak wstępnego zatwierdzania, który uruchamia checkbashismsnarzędzie z pakietu devscripts Debiana na każdymsh pliku w repozytorium, aby upewnić się, że nie wprowadzę przypadkowo składni specyficznej dla Bash. Rodzi to jednak błąd dla mojego .profile:

possible bashism in .profile line 51 ($BASH_SOMETHING):
if [ "${BASH_VERSION:-}" ]; then

Zastanawiałem się, czy istnieje sposób sprawdzenia, która powłoka jest uruchomiona, co nie wywołałoby ostrzeżenia checkbashisms.

Sprawdziłem listę zmiennych związanych powłoką wymienionych przez POSIX w nadziei, że jedna z nich mogłaby posłużyć do wyświetlenia bieżącej powłoki. Przyjrzałem się również zmiennym ustawionym w interaktywnej powłoce Dash, ale znowu nie udało mi się znaleźć odpowiedniego kandydata.

W tej chwili wykluczyłem .profileprzetwarzanie checkbashisms; to mały plik, więc nie jest trudno go sprawdzić ręcznie. Jednak po zbadaniu problemu nadal chciałbym wiedzieć, czy istnieje metoda zgodna z POSIX, aby określić, która powłoka działa (lub przynajmniej sposób, który nie powoduje checkbashismsawarii).


Dalsze informacje / wyjaśnienia

Jednym z powodów, dla których poddaję pliki konfiguracyjne powłoki kontroli wersji, jest konfiguracja środowiska we wszystkich systemach, na których obecnie loguję się regularnie: Cygwin, Ubuntu i CentOS (zarówno 5, jak i 7, używając Active Directory dla użytkownika poświadczenie). Najczęściej loguję się za pośrednictwem środowisk X Windows / Desktop i SSH dla hostów zdalnych. Chciałbym jednak, aby był to dowód na przyszłość i jak najmniej polegał na zależnościach systemu i innych narzędziach.

Używam checkbashismsjako prostego, automatycznego sprawdzania poprawności składni plików związanych z powłoką. Nie jest to idealne narzędzie, np. Już nałożyłem na niego łatkę, aby nie narzekała na użycie command -vw moich skryptach. Podczas badań dowiedziałem się, że rzeczywistym celem programu jest zapewnienie zgodności z polityką Debiana, która, jak rozumiem, opiera się na POSIX 2004, a nie na 2008 (lub jego wersji z 2013 roku).

Anthony G - sprawiedliwość dla Moniki
źródło
To, co robisz, jest tak zgodne z POSIX, jak może być, gdy chodzi o to, aby działać inaczej w różnych środowiskach zgodnych z POSIX. Wasza wołowina jest z checkbashism.
Gilles „SO- przestań być zły”
A tak przy okazji, nigdy nie używałem checkbashism do sprawdzania przenośności mojej konfiguracji. Sprawdzam to, używając go w różnych systemach.
Gilles 'SO - przestań być zły'
2
Nawiasem mówiąc, dla konkretnej rzeczy, którą tutaj robisz, napisz .bash_profileźródło, które zarówno .profile(i warunkowo) .bashrc.
Gilles „SO- przestań być zły”
Dzięki za opinie @Gilles. To bardzo eleganckie rozwiązanie dla konkretnego problemu.
Anthony G - sprawiedliwość dla Moniki

Odpowiedzi:

16

Twój

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

kod jest całkowicie zgodny z POSIX i jest to najlepszy sposób na sprawdzenie, czy aktualnie działasz bash. Oczywiście $BASH_VERSIONzmienna jest specyficzna dla basha, ale właśnie dlatego jej używasz! Aby sprawdzić, czy biegnieszbash !

Pamiętaj, że $BASH_VERSIONzostanie ustawione, czy bashbędzie wywoływane jako bashczy sh. Po upewnieniu się, że działasz bash, możesz użyć [ -o posix ]jako wskaźnika, że ​​powłoka została wywołana jako sh(chociaż ta opcja jest również ustawiona, gdy POSIXLY_CORRECT znajduje się w środowisku lub bashjest wywoływane z -o posix, lub z SHELLOPTS = posix w środowisku. Ale we wszystkich tych przypadkach bashzachowuje się tak, jakby był nazywany jak sh).


Inna zmienna, której możesz użyć zamiast $BASH_VERSIONi checkbashismwydaje się, że nie narzeka, chyba że zostanie podana -xopcja $BASH. Jest to również specyficzne dla bashtego, z którego powinieneś być w stanie określić, czy biegasz, bashczy nie.


Twierdziłbym również, że tak naprawdę nie jest to właściwe użycie checkbashisms. checkbashismsjest narzędziem pomagającym pisać przenośne shskrypty (zgodnie ze shspecyfikacją w polityce Debiana, nadzbiorem POSIX), pomaga zidentyfikować niestandardową składnię wprowadzoną przez osoby piszące skrypty w systemach, w których shznajduje się dowiązanie symboliczne dobash .

A .profilejest interpretowane przez różne powłoki, z których wiele nie jest zgodnych z POSIX. Ogólnie rzecz biorąc, nie należy używać shjako powłokę logowania, ale muszle podoba zsh, fishczy bashz bardziej zaawansowanych funkcji interaktywnych.

bashi zshgdy nie są wywoływane jako shi kiedy ich odpowiedni plik sesji profilu ( .bash_profile, .zprofile) nie jest zgodny z POSIX (szczególnie zsh), ale nadal jest czytany .profile.

Więc nie jest to składnia POSIX, którą chcesz, .profileale składnia zgodna z POSIX (dla sh), basha zshjeśli kiedykolwiek będziesz używać tych powłok (być może nawet Bourne, ponieważ powłoka Bourne również czyta, .profileale nie jest powszechnie spotykana w systemach Linux) ).

checkbashismsna pewno pomogłoby ci znaleźć baszty, ale może nie wskazywać składni POSIX, która nie jest kompatybilna z zshlubbash .

Tutaj, jeśli chcesz użyć bashkodu -specyficznego (jak obejście tego bashbłędu, dzięki któremu nie czyta się ~/.bashrcw interaktywnych powłokach logowania), lepszym rozwiązaniem byłoby to ~/.bash_profilezrobić (przed lub po sourcowaniu, ~/.profilegdzie umieszczasz swoją wspólną sesję inicjalizacje).

Stéphane Chazelas
źródło
To nie przechodzi z checkbashismspowodu zastosowanej zmiennej.
Thomas Dickey,
2
@ThomasDickey, tak, ale to jest przypadek, w którym raport o sprawdzianach powinien zostać zignorowany. Wszystkie pozostałe dotychczas podane rozwiązania są znacznie gorsze.
Stéphane Chazelas
@ StéphaneChazelas Mówisz, że wszystkie inne rozwiązania są gorsze. Co byłoby złego w korzystaniu $0z tego konkretnego przypadku?
Anthony G - sprawiedliwość dla Moniki
2
@AnthonyGeoghegan To nie rozróżnia między bash o nazwie as sha inną powłoką o nazwie as sh.
Gilles „SO- przestań być zły”
Daje również fałszywie dodatni wynik, jeśli dashniepoprawnie wywołano inną powłokę niebędącą powłoką bash argv[0] == "bash", co jest całkowicie legalne, jeśli jest mało prawdopodobne.
Kevin
17

Zwykle używa się $0do tego celu. Na stronie, którą podlinkowałeś, stoi:

0
   (Zero.) Expands to the name of the shell or shell script.
jimmij
źródło
Tak prosty! Przyzwyczaiłem się do używania $0nazwy skryptu powłoki, więc czyściłem, zapomniałem, że może ona również odnosić się do bieżącej powłoki. Dzięki!
Anthony G - sprawiedliwość dla Moniki
1
Jak zwykle, konwencja ta jest przestrzegana przez wszystkie dobrze zachowujące się programy, ale szkodliwy program może z Tobą zadzierać, używając różnych wywołań systemowych z execrodziny, ponieważ pozwalają programowi wywołującemu zidentyfikować uruchomienie pliku binarnego oddzielnie od łańcucha przekazywanego jako argument 0.
dmckee
To działa na .profile :) uroczy
Rob
5

Zwykle zmienna środowiskowa SHELL informuje o domyślnej powłoce. Nie powinno być konieczne ręczne źródło pliku .bashrc (nieprawda, patrz aktualizacja poniżej), bash powinien to zrobić automatycznie, po prostu upewnij się, że znajduje się on w katalogu $ HOME.

Inną opcją jest zrobienie czegoś takiego:

 ps -o cmd= $$

która powie ci polecenie (bez argumentów, = bez wartości nie wyświetli nagłówków kolumn) bieżącego procesu. Przykładowe dane wyjściowe:

 $ps -o cmd= $$
 bash
 $sh
 $ps -o cmd= $$
 sh

AKTUALIZACJA:

Poprawiono mnie! :)

Plik .bashrc nie zawsze jest pozyskiwany, jak wspomniano w komentarzach poniżej /programming/415403/whats-the-difference-between-bashrc-bash-profile-and-environment

Możesz więc przenieść .bashrc do .bash_profile i sprawdzić, czy to działa bez konieczności wykonywania testu. Jeśli nie, masz powyższy test.

Obrabować
źródło
1
.bashrcnie jest faktycznie pozyskiwany przez powłokę logowania, ale .profile(jeśli istnieje) lub .bash_profilesą. SHELLnie jest określony przez POSIX i niestety nie ma też cmdopcji formatowania dla ps.
Anthony G - sprawiedliwość dla Moniki
1
Tego bym użył, ale jest również przedmiotem złośliwej manipulacji.
dmckee,
Zauważ, że chociaż ps -o cmd= $$powinno działać prawie wszędzie, $SHELLzmienna jest całkowicie nieistotna. Jest to domyślna powłoka użytkownika i nie ma wpływu na to, która powłoka jest obecnie uruchamiana lub jaka powłoka uruchamia skrypt powłoki.
terdon
2
Nie, naprawdę nie. wiele powłok będzie czytać ~/.profilepo uruchomieniu jako powłoki logowania. Na przykład możesz $SHELLbyć cshi biegniesz bash -l. To zacznie bash jako powłokę logowania, więc będzie czytać ~/.profile, ale $SHELLnadal będziesz wskazywać csh. Ponadto .profilemoże być pozyskiwany jawnie przez inny skrypt. Ponieważ PO dąży do maksymalnej niezawodności, należy rozważyć wszystkie rodzaje przypadków. W każdym razie $SHELLnie dostarcza żadnych użytecznych informacji o aktualnie działającej powłoce.
terdon
2
FWIW, ja też jestem poprawiony - o SHELLtym, że nie został określony przez POSIX: pubs.opengroup.org/onlinepubs/9699919799/basedefs/...
Anthony G - sprawiedliwość dla Moniki
4

Pytanie dotyczy powłoki logowania użytkownika oraz bieżącej powłoki w sposób, który uspokaja checkbashisms. Jeśli ma to być powłoka, na której zalogował się użytkownik, użyłbym tej z /etc/passwdnp.

MY_UID=$(id -u)
MYSHELL=$(awk -F: '$3 == '$MY_UID'{ print $7; }' </etc/passwd )

Użytkownicy mogą oczywiście uruchomić nową powłokę po zalogowaniu. Oczywiście, jeśli jedna jest bash, a druga nie, testy zmiennych środowiskowych bash mogą nie pomóc.

Niektórzy mogą chcieć użyć getentzamiast passwdpliku (ale nie byłoby to objęte pytaniem).

W oparciu o komentarz na temat LDAP i sugestię lognamemożna użyć tej alternatywnej formy:

MY_NAME=$(logname)
MYSHELL=$(getent passwd | awk -F: '$1 ~ /^'$MY_NAME'$/ {print $7;}' )

Podczas testowania zauważyłem, że lognamenie podoba mi się przekierowanie danych wejściowych (więc zostawiłem wyrażenie podzielone). Pokazy szybkiej kontroli getentpowinny działać na wymienionych platformach (choć powinno to być zawarte w pierwotnym pytaniu):

Thomas Dickey
źródło
1
Zakładając, że baza danych użytkowników znajduje się w / etc / passwd (nie LDAP / NIS / mysql ...) i że istnieje tylko jedna nazwa użytkownika na identyfikator użytkownika. (lepiej byłoby porównać pierwszą kolumnę $(logname)).
Stéphane Chazelas
iirc, POSIX nie definiuje potrzebnego narzędzia (lub użyłbym go w mojej odpowiedzi). To, czy użyć, idczy modyfikować przez użytkownika zmienną, to inny wybór.
Thomas Dickey,
1
Zauważ, że powiedziałem $(logname), nie $LOGNAME. Identyfikator użytkownika i powłoka logowania pochodzą z nazwy użytkownika użytej podczas logowania. Nie można wrócić z identyfikatora użytkownika do powłoki logowania, z wyjątkiem systemów, w których istnieje tylko jedna nazwa użytkownika na identyfikator użytkownika. Lub IOW, kluczem podstawowym w bazie danych użytkowników jest nazwa użytkownika, a nie identyfikator użytkownika.
Stéphane Chazelas
Dzięki @ThomasDickey za odpowiedź pochodzącą z innej perspektywy. Jest to jednak trochę bardziej skomplikowane niż to, o czym myślałem (bardziej jak odpowiedzi Jimmiego i Stéphane'a ). Jednym z powodów, dla których poddaję pliki konfiguracyjne powłoki kontroli wersji, jest konfiguracja środowiska we wszystkich systemach, na których obecnie loguję się regularnie: Cygwin, Ubuntu i CentOS (zarówno 5, jak i 7, używając Active Directory dla użytkownika poświadczenie). Z tego powodu wolę, aby logika była jak najprostsza.
Anthony G - sprawiedliwość dla Moniki