Dlaczego bashrc sprawdza, czy bieżąca powłoka jest interaktywna?


62

W moim Arch zainstaluj /etc/bash.bashrci /etc/skel/.bashrczawierają te linie:

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

W Debianie /etc/bash.bashrcma:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

I /etc/skel/.bashrc:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

Według man bashnieinteraktywnych powłok nawet nie czytają tych plików:

   When  bash  is  started  non-interactively,  to run a shell script, for
   example, it looks for the variable BASH_ENV in the environment, expands
   its  value if it appears there, and uses the expanded value as the name
   of a file to read and execute.  Bash behaves as if the  following  com
   mand were executed:
          if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
   but  the value of the PATH variable is not used to search for the file
   name.

Jeśli dobrze rozumiem, *.bashrcpliki zostaną odczytane tylko wtedy, gdy BASH_ENVjest ustawione na ich wskazywanie. Jest to coś, co nie może się zdarzyć przypadkowo i nastąpi tylko wtedy, gdy ktoś wyraźnie ustawił odpowiednio zmienną.

Wydaje się, że łamie to możliwość .bashrcautomatycznego uruchamiania skryptów przez użytkownika, ustawiając BASH_ENVcoś, co może się przydać. Biorąc pod uwagę, że bash nigdy nie odczyta tych plików, gdy zostaną uruchomione bez interakcji, chyba że zostanie to wyraźnie nakazane, dlaczego *bashrcpliki domyślne nie pozwalają na to?


5
Aby uzyskać praktyczną odpowiedź, zobacz SCP bezbłędnie
Gilles,

Odpowiedzi:


69

To pytanie zamieściłem tutaj kilka tygodni temu. Podobnie jak terdon , zrozumiałem, że a .bashrcjest pozyskiwane tylko dla interaktywnych powłok Bash, więc nie powinno być potrzeby .bashrcsprawdzania, czy działa w interaktywnej powłoce. Mylące jest to, że wszystkie używane przeze mnie dystrybucje (Ubuntu, RHEL i Cygwin) miały pewien rodzaj sprawdzania (testowania $-lub $PS1), aby upewnić się, że bieżąca powłoka jest interaktywna. Nie lubię programowania kultowego, więc postanowiłem zrozumieć cel tego kodu .bashrc.

Bash ma specjalny futerał na zdalne powłoki

Po zbadaniu problemu odkryłem, że odległe powłoki są traktowane inaczej. Chociaż nieinteraktywne powłoki Bash zwykle nie uruchamiają ~/.bashrcpoleceń podczas uruchamiania, zachodzi szczególny przypadek, gdy powłoka jest wywoływana przez zdalny demon powłoki :

Bash próbuje ustalić, kiedy jest uruchamiany ze standardowym wejściem podłączonym do połączenia sieciowego, tak jak w przypadku wykonania przez zdalny demon powłoki, zwykle rshdlub bezpieczny demon powłoki sshd. Jeśli Bash ustali, że jest uruchamiany w ten sposób, czyta i wykonuje polecenia z ~ / .bashrc, jeśli plik ten istnieje i jest czytelny. Nie zrobi tego, jeśli zostanie wywołany jako sh. --norcOpcja może być stosowane do hamowania to zachowanie, a --rcfileopcja może być wykorzystane do wymuszenia innego pliku do odczytania, ale ani rshdnie sshdwywołuje powłoki z tymi opcjami lub mogły być określone.

Przykład

Wstaw następujące na początku pilota .bashrc. (Jeśli .bashrcpochodzi z .profilelub .bash_profile, tymczasowo wyłącz to podczas testowania):

echo bashrc
fun()
{
    echo functions work
}

Uruchom następujące polecenia lokalnie:

$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
  • Brak iw $-oznacza, że ​​powłoka jest nieinteraktywna .
  • Nie prowadząc -w $0wskazuje, że powłoka nie jest powłoką zgłoszeniową .

.bashrcMożna również uruchamiać funkcje powłoki zdefiniowane w pilocie :

$ ssh remote_host fun
bashrc
functions work

Zauważyłem, że ~/.bashrcjest ono pozyskiwane tylko wtedy, gdy jako argument dla podano polecenie ssh. Ma to sens: kiedy sshsłuży do uruchomienia zwykłej powłoki logowania .profilelub .bash_profilejest uruchamiany (i .bashrcjest pozyskiwany tylko wtedy, gdy jest to jawnie zrobione przez jeden z tych plików).

Główną korzyścią, jaką widzę po uzyskaniu .bashrcźródła podczas uruchamiania (nieinteraktywnego) polecenia zdalnego, jest możliwość uruchamiania funkcji powłoki. Jednak większość poleceń w typowym .bashrcprzypadku dotyczy tylko interaktywnej powłoki, np. Aliasy nie są rozwijane, chyba że powłoka jest interaktywna.

Zdalne przesyłanie plików może się nie powieść

Zwykle nie stanowi to problemu, gdy rshlub sshsą używane do uruchomienia interaktywnej powłoki logowania lub gdy nieinteraktywne powłoki są używane do uruchamiania poleceń. Jednak to może być problem dla programów takich jak rcp, scpi sftpktóre wykorzystują zdalne muszle do przesyłania danych.

Okazuje się, że domyślna powłoka zdalnego użytkownika (jak Bash) jest domyślnie uruchamiana podczas korzystania z scppolecenia. Nie ma wzmianki o tym na stronie podręcznika - tylko wzmianka, która scpwykorzystuje się sshdo przesyłania danych. Powoduje to, że jeśli .bashrczawiera jakiekolwiek polecenia wypisywane na standardowe wyjście, przesyłanie plików zakończy się niepowodzeniem , np. Scp nie powiedzie się bez błędu .

Zobacz także pokrewny raport o błędach Red Hat sprzed 15 lat, scp psuje się, gdy w / etc / bashrc znajduje się polecenie echa (które ostatecznie zostało zamknięte jako WONTFIX).

Dlaczego scpi sftpnie

SCP (Secure copy) i SFTP (Secure File Transfer Protocol) mają własne protokoły dla lokalnego i zdalnego końca do wymiany informacji o przesyłanych plikach. Każdy nieoczekiwany tekst ze zdalnego końca jest (nieprawidłowo) interpretowany jako część protokołu i przesyłanie nie powiedzie się. Według FAQ z Snail Book

Co często zdarza się jednak, że są stwierdzenia zawarte w każdym systemie lub pliki startowe powłoki na użytkownika na serwerze ( .bashrc, .profile, /etc/csh.cshrc, .login, itp), które wysyłają wiadomości tekstowe przy logowaniu, przeznaczona do odczytania przez człowieka (takich jak fortune, echo "Hi there!", itp.).

Taki kod powinien generować dane wyjściowe przy logowaniu interaktywnym tylko wtedy, gdy jest ttydołączone do standardowego wejścia. Jeśli nie wykona tego testu, wstawi te wiadomości tekstowe tam, gdzie nie należą: w tym przypadku, zanieczyszczając strumień protokołu między scp2/ sftpa sftp-server.

Powodem, dla którego pliki startowe powłoki są w ogóle istotne, jest to, że sshd używa powłoki użytkownika podczas uruchamiania jakichkolwiek programów w imieniu użytkownika (przy użyciu np. / Bin / sh -c „polecenia”). Jest to tradycja uniksowa i ma zalety:

  • Zwykłe ustawienia użytkownika (aliasy poleceń, zmienne środowiskowe, umask itp.) Obowiązują podczas uruchamiania poleceń zdalnych.
  • Powszechna praktyka ustawiania powłoki konta na / bin / false, aby ją wyłączyć, uniemożliwi właścicielowi uruchamianie jakichkolwiek poleceń, jeśli z jakiegoś powodu uwierzytelnianie nadal będzie przypadkowo pomyślne.

Szczegóły protokołu SCP

Dla osób zainteresowanych szczegółami działania SCP znalazłem ciekawe informacje w artykule Jak działa protokół SCP, który zawiera szczegółowe informacje na temat Uruchamiania scp z gadatliwymi profilami powłoki po drugiej stronie? :

Może się to na przykład zdarzyć, jeśli dodasz to do profilu powłoki w systemie zdalnym:

Echo ""

Dlaczego po prostu się zawiesza? Wynika to ze sposobu, scpw jaki w trybie źródłowym czeka na potwierdzenie pierwszego komunikatu protokołu. Jeśli nie jest to wartość binarna 0, oczekuje, że jest to powiadomienie o zdalnym problemie i czeka, aż więcej znaków utworzy komunikat o błędzie, aż nadejdzie nowa linia. Ponieważ nie wydrukowałeś kolejnej nowej linii po pierwszej, Twój lokalny scppo prostu pozostaje w pętli, zablokowany read(2). W międzyczasie, po przetworzeniu profilu powłoki po stronie zdalnej, scpuruchomiono tryb sink, który również się blokuje read(2), czekając na binarne zero oznaczające początek przesyłania danych.

Wniosek / TLDR

Większość instrukcji w typowym przypadku .bashrcjest użyteczna tylko dla interaktywnej powłoki - nie podczas uruchamiania poleceń zdalnych za pomocą rshlub ssh. W większości takich sytuacji ustawianie zmiennych powłoki, aliasów i definiowanie funkcji nie jest pożądane - a drukowanie dowolnego tekstu na standard jest aktywnie szkodliwe, jeśli pliki są przesyłane za pomocą programów takich jak scplub sftp. Wyjście po sprawdzeniu, że bieżąca powłoka jest nieinteraktywna, jest najbezpieczniejszym zachowaniem .bashrc.


fajny artykuł. ale powinienem to zrobić? Mam odprawę przez .bashrc, ale nadal scp kończy działanie z kodem 0 i nie kopiuję niczego do zdalnej skrzynki
ses

@ses Najlepiej byłoby zadać nowe pytanie, w którym można bardziej szczegółowo opisać swoją sytuację.
Anthony Geoghegan

16

Strona podręcznika nie wspomina o bashźródłach .bashrcnieinteraktywnych zdalnych powłok, jak w

ssh hostname command

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1010

 COMMAND        EXECUTE BASHRC
 --------------------------------
 bash -c foo        NO
 bash foo           NO
 foo                NO
 rsh machine ls     YES (for rsh, which calls 'bash -c')
 rsh machine foo    YES (for shell started by rsh) NO (for foo!)
 echo ls | bash     NO
 login              NO
 bash               YES

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1050

          /* If we were run by sshd or we think we were run by rshd, execute
             ~/.bashrc if we are a top-level shell. */
          if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2)
            {
              maybe_execute_file (SYS_BASHRC, 1);
              maybe_execute_file (bashrc_file, 1);

Czy pobierasz .bashrc z .bash_profile (lub .profile)?
glenn jackman

Tak, ale nie o to chodzi. Pusty .bash_profilepokazałby to samo zachowanie.
Mikel

Nie jestem pewien, co to pokazuje. Mówisz, że rsh(z których nigdy nie korzystałem) źródła ~/.bashrc? A przez to, że dystrybucje Linuksa blokują go na bashwypadek, gdyby ktoś próbował uruchomić skrypt rsh? ssh servernie powinien się dotykać, .bashrcponieważ i tak jest to powłoka logowania. Nie, chyba że twój system jest jednym z tych, które źródło ~ / .bashrc` ~/.profile. I ssh server commandnawet nie powinienem tego robić. Na pewno nie ma w moim systemie.
terdon

2
Nie. Połączyłem cię ze bashźródłem, a nie ze rshźródłem. :) Komentarz dotyczy sshrównież, został napisany bardzo dawno temu. Zobacz zaktualizowaną odpowiedź z większym źródłem.
Mikel

Ach, to pomocne, dziękuję. Jak mam to przetestować? Próbowałem dodać echona samym szczycie /etc/bash.bashrci ~/.bashrc(przed interaktywnymi testami powłoki), a następnie ssh na maszynie docelowej za pomocą ssh server hostnamei podczas hostnamedziałania, nie widziałem żadnego z moich ech. Czy to, że mój shell_level(cokolwiek to jest) nie jest <2?
terdon

5

Zgodnie z konwencją .bashrcto miejsce, w którym użytkownik przechowuje niestandardową konfigurację powłoki.

Tymi dostosowanymi konfiguracjami mogą być zmienne środowiskowe, aliasy, fantazyjne monity. Z nieinteraktywną powłoką te rzeczy są bez znaczenia. Co więcej, nieinteraktywna powłoka może być wywoływana w wielu kontekstach, nie jesteś pewien, że te zmienne środowiskowe mogą prowadzić do fałszywie ujemnych liter, a nawet podatności na zagrożenia.

Najbliższym przykładem jest alias taki jak:

alias cp='cp -i'

Następnie zawiesi na zawsze twoją nieinteraktywną powłokę.

Sprawdzanie .bashrcodbywa się u góry, aby upewnić się, że nie wystąpią problemy.


Ponieważ powłoka może być wywoływana jako nieinteraktywna powłoka logowania , więc jawne blokowanie pozyskiwania *bashrcnie ma sensu.

Gdy powłoka została wywołana jako nieinterakcyjnym powłoki logowania , to źródło /etc/profile, a następnie pozyskać pierwszy znaleziony w ~/.bash_profile, ~/.bash_logini ~/.profile:

Kiedy bash jest wywoływany jako interaktywna powłoka logowania lub jako nieinteraktywna powłoka z opcją --login , najpierw czyta i wykonuje polecenia z pliku / etc / profile, jeśli plik ten istnieje. Po odczytaniu tego pliku szuka ~ / .bash_profile, ~ / .bash_login i ~ / .profile, w tej kolejności, i odczytuje i wykonuje polecenia z pierwszego, który istnieje i jest czytelny.

Nic nie stoi na przeszkodzie, aby te pliki .bashrcsame się pozyskiwały , więc sprawdzanie w środku .bashrcjest bezpieczniejsze i upraszcza sprawę.


Tak, wiem to. Właśnie dlatego powłoki nieinteraktywne w ogóle nie źródło *bashrcplików. Moje pytanie brzmi: dlaczego różni dostawcy Linuksa mają problem z jawnym blokowaniem nieinteraktywnych powłok przed odczytaniem tych plików, jeśli te pliki i tak nie są odczytywane przez powłoki nieinteraktywne.
terdon

1
@terdon: Ponieważ powłoka może być wywoływana jako nieinteraktywna powłoka logowania , wówczas powłoka logowania będzie pozyskiwać źródło .bash_profile, które może być źródłem .bashrc.
cuonglm

Nie, w nieinteraktywnych powłokach jedyne, co jest czytane, to $BASH_ENV. Zarówno *profilei *bashrcsą ignorowane. A przynajmniej tak mówi strona podręcznika. Jednak, jak pokazał Mikel, strona podręcznika użytkownika może kłamać.
terdon

@terdon: Przeczytaj moją zaktualizowaną odpowiedź z linkiem w niej. Czy próbowałeś bash -l -c :wywołać nieinteraktywną powłokę logowania?
cuonglm

Łał. Masz całkowitą rację. Przeczytałem tę część około --login100 razy, ale najwyraźniej nigdy się nie zarejestrowała. Dzięki!
terdon
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.