Cel [-n „$ PS1”] w bashrc

10

Jaki cel robi [ -n "$PS1" ]się [ -n "$PS1" ] && source ~/.bash_profile;służyć? Ta linia jest zawarta w repozytorium.bashrc dotfiles .

strzały
źródło

Odpowiedzi:

20

Sprawdza to, czy powłoka jest interaktywna, czy nie. W takim przypadku pozyskiwanie ~/.bash_profilepliku jest możliwe tylko wtedy, gdy powłoka jest interaktywna.

Zobacz „Czy to Shell Interactive?” w podręczniku bash, który cytuje ten konkretny idiom. (Zaleca się również sprawdzenie, czy powłoka jest interaktywna, poprzez sprawdzenie, czy $-zmienna specjalna zawiera iznak, co jest lepszym podejściem do tego problemu).

filbranden
źródło
bash przynajmniej rozbroi PS1 i PS2, jeśli powłoka jest interaktywna . Możesz to zobaczyć na własne oczy, używając ( export PS1='abc$ '; bash -c 'echo "[$PS1]"' ), które po prostu drukuje []. Wydaje zsh nie to samo, co najmniej z doświadczenia ... W każdym razie zamiar z [ -n "$PS1" ]jest sprawdzenie, czy powłoka jest interaktywna, czy nie.
filbranden
3
To bashrozbija PS1, gdy nieinteraktywny (literówka w poprzednim komentarzu) jest błędem IMO, PS1 nie jest zmienną specyficzną dla bash, nie ma biznesu, który mógłby go rozbroić. Jest to jedyna powłoka, która to robi (chociaż yashustawia także PS1wartość domyślną, nawet gdy nie jest interaktywna).
Stéphane Chazelas,
1
Ponieważ kod, o którym mowa, znajduje się w pliku specyficznym dla basha, wydaje się to rozsądną odpowiedzią. Inne odpowiedzi dotyczą bardziej ogólnego przypadku specyfikacji POSIX lub innych powłok. Odpowiedziałeś „jaki jest tego cel?” zamiar w pytaniu, jednocześnie odpowiednio odkładając na bok resztę. Dobrze jest wiedzieć, co robi bash i być może lepsze sposoby na osiągnięcie celu.
Jeff Schaller
Uznałbym tę odpowiedź za kompletniejszą, gdyby sugerowała bardziej wiarygodną alternatywę (powiedzmy [[ $- = *i* ]] && source ~/.bash_profile).
Charles Duffy,
@CharlesDuffy Szczerze mówiąc, nie sądzę, żeby było w tym wiele złego [ -n "${PS1}" ], ale wciąż aktualizowałem swoją odpowiedź, aby podkreślić, że instrukcja bash sugeruje / zaleca sprawdzenie, $-czy powłoka jest interaktywna, mam nadzieję, że znajdziesz lepszą odpowiedź. Twoje zdrowie!
filbranden
19

Co to robi?

Jest to szeroko rozpowszechniony sposób testowania, czy powłoka jest interaktywna. Uważaj, że działa tylko w trybie bash, nie działa z innymi powłokami. Więc jest w porządku (choć głupie) .bashrc, ale nie działałoby .profile(co jest czytane przez sh, a bash jest tylko jedną z możliwych implementacji sh, a nie najczęstszą).

Dlaczego to działa (tylko w bash!)

Interaktywna powłoka ustawia zmienną powłokiPS1 na domyślny ciąg znaków zachęty. Więc jeśli powłoka jest interaktywna, PS1jest ustawiona (chyba że użytkownik .bashrcją usunął, co nie mogło się jeszcze zdarzyć na górze .bashrc, a można by uznać, że i tak jest to głupie).

Odwrotna sytuacja jest prawdziwa w przypadku bash: nieinteraktywne instancje bash są rozbrojone PS1po uruchomieniu. Zauważ, że to zachowanie jest specyficzne dla bash i jest prawdopodobnie błędem (dlaczego bash -c '… do stuff with $var…'nie miałoby działać, kiedy varjest PS1?). Ale robią to wszystkie wersje bash do 4.4 (najnowsza wersja jak piszę).

Wiele systemów eksportuje PS1do środowiska. Jest to zły pomysł, ponieważ wiele różnych powłok używa, PS1ale ma inną składnię (np . Szybkie zmiany znaczenia bash są całkowicie różne od szybkich zmian znaczenia zsh ). Ale jest wystarczająco rozpowszechniony, że w praktyce sprawdzenie, czy PS1jest ustawione, nie jest wiarygodnym wskaźnikiem interaktywności powłoki. Powłoka mogła odziedziczyć PS1po środowisku.

Dlaczego jest (źle) używany tutaj

.bashrcto plik, który bash czyta podczas uruchamiania, gdy jest interaktywny. Mniej znanym faktem jest to, że bash czyta również .bashrcpowłokę logowania, a heurystyka basha stwierdza, że ​​jest to sesja zdalna (bash sprawdza, czy jej rodzicem jest rshdlub sshd). W tym drugim przypadku jest mało prawdopodobne, PS1aby został ustawiony w środowisku, ponieważ nie został jeszcze uruchomiony żaden plik kropkowy.

Jednak sposób, w jaki kod wykorzystuje te informacje, przynosi efekt przeciwny do zamierzonego.

  • Jeśli powłoka jest powłoką interaktywną, wówczas działa ona .bash_profilew tej powłoce. Ale .bash_profileto skrypt czasu logowania. Może uruchamiać niektóre programy, które mają być uruchamiane tylko raz na sesję. Może to zastąpić niektóre zmienne środowiskowe, które użytkownik celowo ustawił na inną wartość przed uruchomieniem tej powłoki. Uruchamianie .bash_profilew powłoce niezalogowanej jest uciążliwe.
  • Jeśli powłoka jest nieinteraktywną powłoką zdalnego logowania, nie zostanie załadowana .bash_profile. Ale jest to przypadek, w którym ładowanie .bash_profilemoże być przydatne, ponieważ nieinteraktywna powłoka logowania nie ładuje się automatycznie /etc/profilei ~/.profile.

Myślę, że ludzie robią to dla użytkowników, którzy logują się przez GUI (bardzo częsty przypadek) i .bash_profilezamiast tego wprowadzają ustawienia zmiennych środowiskowych .profile. Większość mechanizmów logowania GUI wywołuje, .profileale nie uruchamia się .bash_profile(czytanie .bash_profilewymagałoby uruchomienia bash w ramach uruchamiania sesji, zamiast sh). W tej konfiguracji, gdy użytkownik otworzy terminal, otrzyma swoje zmienne środowiskowe. Jednak użytkownik nie otrzyma swoich zmiennych środowiskowych w aplikacjach GUI, co jest bardzo częstym źródłem zamieszania. Rozwiązaniem jest użycie .profilezamiast .bash_profileustawiania zmiennych środowiskowych. Dodanie pomostu .bashrci .bash_profilestwarza więcej problemów niż rozwiązuje.

Co zamiast tego zrobić

Istnieje prosty, przenośny sposób testowania, czy bieżąca powłoka jest interaktywna: -isprawdź, czy opcja jest włączona.

case $- in
  *i*) echo "This shell is interactive";;
  *) echo "This shell is not interactive";;
esac

Przydaje się to .bashrcdo odczytu .profiletylko wtedy, gdy powłoka nie jest interaktywna - tzn. Odwrotnie niż w kodzie! Przeczytaj, .profileczy bash jest (nieinteraktywną) powłoką logowania i nie czytaj jej, jeśli jest to powłoka interaktywna.

if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi
Gilles „SO- przestań być zły”
źródło
4
Warto zauważyć, że lepszym sposobem sprawdzenia, czy powłoka jest interaktywna, jest [[ -o interactive ]](ksh, bash, zsh) lub case $- in (*i*) ...; esac(POSIX)
Stéphane Chazelas
2
Mój bash (wersja 4.4.12) wydaje się być rozbrojony, PS1jeśli nie działa interaktywnie. Łatwo jest przetestować: PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'nic nie wydrukuje, a PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'wypisze wartość $PS1zestawu w twoich plikach startowych bash (nie wydrukuje ciągu „kukułka”).
FooF,
1
@ Stéphane Chazelas: POSIX nie wymaga, aby $-zawierał iinteraktywną powłokę.
schily
1
Bosh robi to od 2012 roku, aby być kompatybilnym z ksh. Po prostu nie było to wymagane przez POSIX, dopóki zmiana nie stanie się skuteczna.
schily
1
Szczerze mówiąc, powiedziałbym, że [ -n "${PS1}" ] niewłaściwe dzwonienie posuwa się trochę za daleko, w końcu psuje się tylko wtedy, gdy ktoś eksportuje PS1 (co w twojej odpowiedzi mówisz, że to zły pomysł, a nawet rozumiesz, dlaczego) i to nie ma wpływu i tak bash (ponieważ odznacza PS1 i PS2, jeśli powłoka nie jest interaktywna). Być może lepiej byłoby użyć słowa takiego jak „zniechęcenie” lub mówienie o „ograniczeniach” podejścia. Nie sądzę, żeby to było „złe”. Jeśli coś jest nie tak, to eksport PS1, to na pewno! Tak czy inaczej, dzięki za zapoznanie się ze szczegółami tego.
filbranden
1

Wygląda na to, że ta dziwna koncepcja wynika z faktu, że bashnie zaczął się jako klon powłoki POSIX, ale jako Bourne Shellklon.

W rezultacie zachowanie interaktywne POSIX ( $ENVwywoływane dla interaktywnych powłok) zostało dodane później bashi nie jest powszechnie znane.

Jest jedna powłoka, która zapewnia podobne zachowanie. To jest cshi csh przyznaje, które $promptma określone wartości:

$prompt not set          non-interactive shell, test $?prompt.
$prompt set but == ""    .cshrc called by the which(1) command.
$prompt set and != ""    normal interactive shell.

Ale nie dotyczy to ani powłoki Bourne'a, ani powłok POSIX.

W przypadku powłoki POSIX jedyną dozwoloną metodą jest umieszczenie kodu interaktywnych powłok w pliku:

$ENV

który ma nazwę specyficzną dla powłoki. Jest to np

$HOME/.kshrc    for the korn shell
$HOME/.bashrc   for bash
$HOME/.mkshrc   for mksh
$HOME/.shrc     for the POSIX Bourne Shell

Inne osoby wspomniały o flagi powłoki -i, ale nie jest to użyteczne do niezawodnego programowania. POSIX nie wymaga, aby set -idziałał, ani że $-zawiera ipowłok interaktywnych. POSIX wymaga jedynie, aby sh -iwymusił powłokę w trybie interaktywnym.

Ponieważ zmienna $PS1może być importowana ze środowiska, może mieć wartość nawet w trybie nieinteraktywnym. Fakt, że bash unsetów PS1w dowolnym nieinterakcyjnym skorupy nie jest przyznawana w normie i nie odbywa się przez inną powłokę.

Czyste programowanie (nawet przy pomocy bash) polega na wstawianiu poleceń interaktywnych powłok $HOME/.bashrc.

schily
źródło
0

Najpierw porozmawiam o tym, co Debian, i przez większość czasu także Ubuntu ustawia się na bash. I ten ostatni dotyk w innych systemach.

W ustawieniach plików startowych powłoki jest wiele opinii.
Mam również swoją opinię, ale postaram się pokazać istniejące przykłady prawidłowych ustawień.
Użyję debuan, ponieważ dość łatwo jest znaleźć przykłady jego plików.
Debian jest często używany, więc ustawienia zostały dobrze przetestowane,

Jaki jest cel sprawdzenia, czy PS1 jest ustawiony?

Aby dowiedzieć się, czy powłoka jest interaktywna.

Domyślne /etc/profilew Debianie i Ubuntu (z / usr / share / base-files / profil):

if [ "${PS1-}" ]; then
    if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then

If jest czytany: jeśli interaktywny (domyślny zestaw PS1) i jest to powłoka bash (ale nie działa jako domyślna sh), to zmień PS1 na konkretną nową (nie domyślną).

Domyślny /etc/bash.bashrcDebiana zawiera również:

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

Co jest całkiem jasne w tym, co robi: Jeśli interaktywne nie źródło (reszta).

Jednak w /etc/skel/.bashrcjest przykładem poprawnego sposobu testowania interaktywnej powłoki (za pomocą $-):

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

To powinno jasno pokazać, dlaczego PS1 i jedna alternatywa.

Prawidłowa kolejność

Zgłaszanego ustawienia należy unikać.
Kolejność (od ustawień systemowych do bardziej szczegółowych ustawień użytkownika (bash)) jest /etc/profile, /etc/bash.bashrc, ~/.profilei wreszcie ~/.bashrc. To umieszcza najszersze efekty (i więcej powłok) w /etc/profile(który jest własnością roota), a następnie /etc/bash.bashrc(który jest także własnością roota), ale wpływa tylko na bash. Następnie wprowadź ustawienia osobiste $HOME, pierwsze dotyczy ~/.profilewiększości powłok i ~/.bashrc(prawie równoważne ~/.bash_profile), specyficzne tylko dla bash.

Jest zatem właściwe źródła ~/.bashrcw ~/.profile, to jest przekształcenie ustawienia dla bash do bardziej ogólnego, który jest użytkownikiem konkretnego wpływu na kolejne pociski . Z wyjątkiem sytuacji, gdy wykonano to w ten sposób :

# ~/.profile: executed by the command interpreter for login shells
# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
    fi
fi

Sprawdza, czy bash jest uruchomiony i ładuje się tylko w .bashrctakim przypadku.

Jest to decyzja podjęta przez Debiana. Uzasadnienie wyjaśniono tutaj .

Wręcz przeciwnie, pozyskiwanie ~/.profilew ~/.bash_profile(lub ~/.bashrc) polega jedynie na ponownym stosowaniu ogólnych zasad, które powinny już zostać załadowane do konkretnego przypadku użycia, a zatem „nie jest tak źle” (nie mówię „dobry”). I nie mówię dobrze, ponieważ może to powodować zapętlenie pobierania plików. Podobnie jak w przypadku, gdy podkatalog ładuje element nadrzędny, jest to pętla katalogu.

I właśnie w tym cross-sourcingu sprawdzanie interaktywnej powłoki ma sens. Tylko wtedy, gdy powłoka jest interaktywna, jest ~/.bashrcładowana, ale z kolei może się ładować ~/.profile(lub na odwrót) i w tym przypadku można użyć sprawdzenia powłoki interaktywnej.

Izaak
źródło