Zweck von [-n “$ PS1”] in bashrc

10

Welchen Zweck erfüllt das [ -n "$PS1" ]In [ -n "$PS1" ] && source ~/.bash_profile;? Diese Zeile ist in .bashrceinem Dotfiles- Repo enthalten .

Schüsse
quelle

Antworten:

20

Hiermit wird überprüft, ob die Shell interaktiv ist oder nicht. In diesem Fall wird die ~/.bash_profileDatei nur bezogen, wenn die Shell interaktiv ist.

Siehe "Ist diese Shell interaktiv?" im Bash-Handbuch, das diese spezifische Redewendung zitiert. (Es wird außerdem empfohlen, zu überprüfen, ob die Shell interaktiv ist, indem getestet wird, ob die $-spezielle Variable das iZeichen enthält. Dies ist ein besserer Ansatz für dieses Problem.)

filbranden
quelle
Bash setzt zumindest PS1 und PS2 zurück, wenn die Shell interaktiv ist . Sie können das selbst sehen, mit ( export PS1='abc$ '; bash -c 'echo "[$PS1]"' )dem einfach gedruckt wird []. Es scheint , zsh nicht das gleiche tun, zumindest aus einem Experiment ... In jedem Fall ist die Absicht der [ -n "$PS1" ]zu prüfen ist , ob die Shell interaktiv ist oder nicht.
Filbranden
3
Das bashDeaktivieren von PS1, wenn es nicht interaktiv ist (Tippfehler in Ihrem vorherigen Kommentar), ist ein IMO-Fehler. PS1 ist keine Bash-spezifische Variable. Es hat kein Geschäft, es zu deaktivieren. Dies ist die einzige Shell, die dies tut (obwohl sie yashauch dann PS1auf einen Standardwert gesetzt wird, wenn sie nicht interaktiv ist).
Stéphane Chazelas
1
Da sich der betreffende Code in einer Bash-spezifischen Datei befindet, scheint dies eine vernünftige Antwort zu sein. Andere Antworten befassen sich mit dem allgemeineren Fall von POSIX-Spezifikationen oder anderen Shells. Sie haben die Frage beantwortet: "Was ist der Zweck davon?" Absicht in der Frage, während der Rest angemessen beiseite gelassen wird. Es ist gut zu wissen, was Bash macht und möglicherweise auch bessere Wege, um das Ziel zu erreichen.
Jeff Schaller
Ich würde diese Antwort als vollständiger betrachten, wenn sie eine zuverlässigere Alternative vorschlagen würde (sagen wir [[ $- = *i* ]] && source ~/.bash_profile).
Charles Duffy
@CharlesDuffy Ehrlich gesagt glaube ich nicht, dass daran viel falsch ist [ -n "${PS1}" ], aber ich habe meine Antwort immer noch aktualisiert, um hervorzuheben, dass das Bash-Handbuch auch eine Inspektion vorschlägt / empfiehlt, $-um festzustellen, ob die Shell interaktiv ist. Ich hoffe, Sie finden, dass dies die Antwort verbessert. Prost!
Filbranden
19

Was das macht

Dies ist eine weit verbreitete Methode, um zu testen, ob die Shell interaktiv ist. Beachten Sie, dass es nur in Bash funktioniert, nicht mit anderen Shells. Es ist also in Ordnung (wenn auch albern) .bashrc, aber es würde nicht funktionieren .profile(was von sh gelesen wird und bash ist nur eine der möglichen Implementierungen von sh und nicht die häufigste).

Warum es funktioniert (nur in Bash!)

Eine interaktive Shell setzt die Shell-VariablePS1 auf die Standard-Eingabeaufforderungszeichenfolge. Wenn die Shell also interaktiv ist, PS1ist sie festgelegt (es sei denn, der Benutzer .bashrchat sie entfernt, was oben noch nicht geschehen ist .bashrc, und Sie könnten davon ausgehen, dass dies sowieso eine dumme Sache ist).

Das Gegenteil ist in bash der Fall: Nicht interaktive Instanzen von bash werden PS1beim Start nicht gesetzt. Beachten Sie, dass dieses Verhalten spezifisch für Bash ist und möglicherweise ein Fehler ist (warum sollte es bash -c '… do stuff with $var…'nicht funktionieren, wenn es varist PS1?). Aber alle Versionen von Bash bis einschließlich 4.4 (die neueste Version, wie ich sie schreibe) tun dies.

Viele Systeme exportieren PS1in die Umwelt. Es ist eine schlechte Idee, da viele verschiedene Shells PS1eine andere Syntax verwenden (z. B. unterscheiden sich die Eingabeaufforderungen von bash von den Eingabeaufforderungen von zsh vollständig ). Es ist jedoch weit genug verbreitet, dass das Festlegen in der Praxis PS1kein verlässlicher Indikator dafür ist, dass die Shell interaktiv ist. Die Shell hat möglicherweise PS1von der Umgebung geerbt .

Warum wird es hier (falsch) verwendet?

.bashrcist die Datei, die Bash beim Start liest, wenn sie interaktiv ist. Eine weniger bekannte Tatsache ist, dass bash auch .bashrceine Anmeldeshell liest und die Heuristiken von bash zu dem Schluss kommen, dass es sich um eine Remote-Sitzung handelt (bash prüft, ob das übergeordnete Element rshdoder ist sshd). In diesem zweiten Fall ist es unwahrscheinlich, dass PS1dies in der Umgebung festgelegt wird, da noch keine Punktdatei ausgeführt wurde.

Die Art und Weise, wie der Code diese Informationen verwendet, ist jedoch kontraproduktiv.

  • Wenn die Shell eine interaktive Shell ist, wird diese .bash_profilein dieser Shell ausgeführt. Ist .bash_profileaber ein Login-Time-Skript. Möglicherweise werden einige Programme ausgeführt, die nur einmal pro Sitzung ausgeführt werden sollen. Möglicherweise werden einige Umgebungsvariablen überschrieben, die der Benutzer vor dem Ausführen dieser Shell absichtlich auf einen anderen Wert festgelegt hat. Das Ausführen .bash_profilein einer Shell ohne Anmeldung ist störend.
  • Wenn es sich bei der Shell um eine nicht interaktive Remote-Anmeldeshell handelt, wird sie nicht geladen .bash_profile. Dies ist jedoch der Fall, wenn das Laden .bash_profilenützlich sein kann, da eine nicht interaktive Anmeldeshell nicht automatisch geladen wird /etc/profileund ~/.profile.

Ich denke , der Grund , warum Menschen diese für die Nutzer zu tun ist , die sich anmelden über eine GUI (eine sehr häufige Fall) und die ihre Einstellungen der Umgebungsvariablen setzen in .bash_profilestatt .profile. Die meisten GUI-Anmeldemechanismen rufen auf, .profileaber nicht .bash_profile(das Lesen .bash_profilewürde das Ausführen von bash als Teil des Sitzungsstarts anstelle von sh erfordern). Wenn der Benutzer bei dieser Konfiguration ein Terminal öffnet, erhält er seine Umgebungsvariablen. Der Benutzer erhält seine Umgebungsvariablen jedoch nicht in GUI-Anwendungen, was sehr häufig zu Verwirrung führt. Die Lösung besteht darin, Umgebungsvariablen zu verwenden, .profileanstatt sie .bash_profilefestzulegen. Das Hinzufügen einer Brücke zwischen .bashrcund .bash_profileschafft mehr Probleme als es löst.

Was ist stattdessen zu tun?

Es gibt eine einfache, tragbare Methode, um zu testen, ob die aktuelle Shell interaktiv ist: Testen Sie, ob die Option -iaktiviert ist.

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

Dies ist nützlich .bashrc, um .profilenur zu lesen , wenn die Shell nicht interaktiv ist - dh das Gegenteil von dem, was der Code tut! Lesen Sie, .profileob bash eine (nicht interaktive) Anmeldeshell ist, und lesen Sie es nicht, wenn es sich um eine interaktive Shell handelt.

if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi
Gilles 'SO - hör auf böse zu sein'
quelle
4
Es könnte erwähnenswert sein, dass ein besserer Weg, um zu testen, ob eine Shell interaktiv ist, mit [[ -o interactive ]](ksh, bash, zsh) oder case $- in (*i*) ...; esac(POSIX)
Stéphane Chazelas
2
Meine Bash (Version 4.4.12) scheint tatsächlich deaktiviert zu sein, PS1wenn sie nicht interaktiv ausgeführt wird. Es ist einfach zu testen: PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'druckt nichts, während PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'der Wert von $PS1set in Ihren Bash-Startdateien gedruckt wird (es wird nicht die Zeichenfolge "cuckoo" gedruckt).
FooF
1
@ Stéphane Chazelas: POSIX verlangt nicht , dass $-enthält ieine interaktive Shell.
schily
1
Bosh tut dies seit 2012, um mit ksh kompatibel zu sein. Es wurde von POSIX nur nicht benötigt, bis die Änderung, die Sie vorgenommen haben, wirksam wird.
schily
1
Ehrlich gesagt würde ich sagen, dass [ -n "${PS1}" ] falsches Anrufen etwas zu weit geht, schließlich bricht es nur ab, wenn jemand PS1 exportiert (was Sie in Ihrer Antwort als schlechte Idee bezeichnen und sogar auf die Gründe dafür eingehen), und das hat keinen Einfluss Bash sowieso (da es PS1 und PS2 deaktiviert, wenn die Shell nicht interaktiv ist.) Vielleicht wäre es besser gewesen, ein Wort wie "entmutigt" zu verwenden oder über die "Einschränkungen" des Ansatzes zu sprechen. Ich denke nicht, dass es insgesamt "falsch" ist. Wenn beim Exportieren von PS1 etwas nicht stimmt, ist das sicher! Wie auch immer, danke, dass Sie auf die Details eingegangen sind.
Filbranden
1

Es scheint, dass dieses seltsame Konzept auf die Tatsache zurückzuführen ist, dass bashes nicht als POSIX-Shell-Klon, sondern als Bourne ShellKlon gestartet wurde .

Infolgedessen wurde das interaktive Verhalten von POSIX ( $ENVwird für interaktive Shells aufgerufen) später hinzugefügt bashund ist nicht allgemein bekannt.

Es gibt eine Shell, die ein ähnliches Verhalten zulässt. Dies ist cshund csh gewährt, $promptdie bestimmte Werte hat:

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

Dies gilt jedoch weder für die Bourne-Shell noch für POSIX-Shells.

Für eine POSIX-Shell besteht die einzige gewährte Methode darin, Code für interaktive Shells in die Datei einzufügen:

$ENV

das hat einen Shell-spezifischen Namen. Es ist zB

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

Andere Leute erwähnten das Shell-Flag -i, aber dies ist für eine zuverlässige Programmierung nicht verwendbar. POSIX erfordert weder, dass dies set -ifunktioniert, noch dass es $-eine ifür interaktive Shells enthält. POSIX erfordert lediglich, dass sh -idie Shell in den interaktiven Modus versetzt wird.

Da die Variable $PS1aus der Umgebung importiert werden kann, kann sie auch im nicht interaktiven Modus einen Wert haben. Die Tatsache, dass bash unsets PS1in einer nicht interaktiven Shell enthalten ist, wird vom Standard nicht gewährt und von keiner anderen Shell ausgeführt.

Saubere Programmierung (auch mit bash) bedeutet also, die Befehle für interaktive Shells einzugeben $HOME/.bashrc.

schily
quelle
0

Ich werde zuerst darüber sprechen, was Debian ist, und die meiste Zeit setzt auch Ubuntu auf Bash. Und letztere berühren andere Systeme.

Bei der Einstellung von Shell-Startdateien gibt es viele Meinungen.
Ich habe auch meine Meinung, aber ich werde versuchen, vorhandene Beispiele für korrekte Einstellungen zu zeigen.
Ich werde Debuan verwenden, da es ziemlich einfach ist, Beispiele für seine Dateien zu finden.
Und Debian wird häufig verwendet, daher wurden die Einstellungen gut getestet.

Was ist das Ziel, um zu überprüfen, ob PS1 eingestellt ist?

Nur um herauszufinden, ob die Shell interaktiv ist.

Die Standardeinstellung /etc/profilein Debian und Ubuntu (aus / usr / share / base-files / profile):

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

Das if lautet: Wenn interaktiv (PS1-Standard festgelegt) und es sich um eine Bash-Shell handelt (die jedoch nicht als Standard fungiert sh), ändern Sie PS1 in eine bestimmte neue (nicht die Standard- Shell ).

Die Standardeinstellung /etc/bash.bashrcin Debian enthält außerdem:

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

Was ziemlich klar ist, was es tut: Wenn interaktiv nicht beschaffen (der Rest).

Jedoch in /etc/skel/.bashrcist ein Beispiel der richtigen Art und Weise zu Test für eine interaktive Shell (mit $-):

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

Das sollte klar zeigen, warum PS1 und eine Alternative.

Die richtige Reihenfolge

Die Einstellung, die Sie melden, sollte vermieden werden.
Die Reihenfolge (von Systemeinstellungen auf spezifischere Benutzereinstellungen (für bash)) ist /etc/profile, /etc/bash.bashrc, ~/.profileund schließlich ~/.bashrc. Dadurch werden die breitesten Effekte (und für mehr Shells) in /etc/profile(die Root gehören) gefolgt von /etc/bash.bashrc(die auch Root gehören) platziert, aber nur Bash beeinflusst. Dann kommen die persönlichen Einstellungen $HOME, die erste ist ~/.profilefür die meisten Muscheln und ~/.bashrc(fast gleichbedeutend mit ~/.bash_profile) nur für Bash spezifisch.

Es ist daher falsch zu Quelle ~/.bashrcin ~/.profile, es einen bestimmten Benutzer für bash auf eine allgemeinere Einstellung verwandelt , die sich mehr Schalen zu beeinflussen . Außer wenn dies folgendermaßen gemacht wird :

# ~/.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

Es überprüft, ob bash ausgeführt wird und lädt nur, .bashrcwenn dies der Fall ist.

Dies ist eine vorgelagerte Entscheidung von Debian. Die Begründung wird hier erläutert .

Umgekehrt werden bei der Beschaffung ~/.profilein ~/.bash_profile(oder ~/.bashrc) nur allgemeine Regeln erneut angewendet, die bereits auf einen bestimmten Anwendungsfall geladen werden sollten, und daher "nicht so schlecht" (ich sage nicht "gut"). Und ich sage nicht gut, weil es dazu führen kann, dass sich das Sourcing von Dateien wiederholt. Wie wenn ein Unterverzeichnis ein übergeordnetes Element lädt, ist dies eine Verzeichnisschleife.

Und in diesem Cross-Sourcing macht die Prüfung auf interaktive Shell Sinn. Nur wenn eine Shell interaktiv ist ~/.bashrc, wird sie geladen, aber sie wird möglicherweise geladen ~/.profile(oder umgekehrt). In diesem Fall kann die Suche nach einer interaktiven Shell verwendet werden.

NotAnUnixNazi
quelle