GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Warum überprüft Bashrc, ob die aktuelle Shell interaktiv ist?

Auf meiner Arch-Installation /etc/bash.bashrc und /etc/skel/.bashrc diese Zeilen enthalten:

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

Unter Debian /etc/bash.bashrc hat:

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

Und /etc/skel/.bashrc :

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

Laut man bash , jedoch lesen nicht interaktive Shells nicht einmal diese Dateien:

   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.

Wenn ich das richtig verstehe, die *.bashrc Dateien werden nur gelesen, wenn BASH_ENV soll auf sie hinweisen. Dies kann nicht zufällig passieren und tritt nur auf, wenn jemand die Variable explizit entsprechend gesetzt hat.

Das scheint die Möglichkeit zu unterbrechen, dass Skripte die .bashrc eines Benutzers beziehen automatisch durch Setzen von BASH_ENV , etwas, das sich als nützlich erweisen könnte. Angesichts der Tatsache, dass bash diese Dateien niemals lesen wird, wenn es nicht interaktiv ausgeführt wird, es sei denn, es wird ausdrücklich dazu aufgefordert, warum sollte dann der Standardwert *bashrc verwendet werden Dateien verbieten es?

Akzeptierte Antwort:

Diese Frage wollte ich hier vor ein paar Wochen stellen. Wie Terdon , ich habe verstanden, dass eine .bashrc wird nur für interaktive Bash-Shells bereitgestellt, daher sollte .bashrc nicht erforderlich sein um zu prüfen, ob es in einer interaktiven Shell läuft. Verwirrenderweise alle Die von mir verwendeten Distributionen (Ubuntu, RHEL und Cygwin) hatten eine Art Überprüfung (Testen von $- oder $PS1 ), um sicherzustellen, dass die aktuelle Shell interaktiv ist. Ich mag keine Cargo-Cult-Programmierung, also habe ich mich daran gemacht, den Zweck dieses Codes in meiner .bashrc zu verstehen .

Bash hat einen Sonderfall für entfernte Shells

Nachdem ich das Problem untersucht hatte, entdeckte ich, dass Remote-Shells unterschiedlich behandelt werden. Während nicht interaktive Bash-Shells normalerweise ~/.bashrc nicht ausführen Befehle beim Start, ein Sonderfall wird gemacht, wenn die Shell von einem entfernten Shell-Daemon aufgerufen wird:

Bash versucht festzustellen, wann es mit seiner Standardeingabe
verbunden mit einer Netzwerkverbindung ausgeführt wird, wie wenn es vom Remote-Shell-Daemon
ausgeführt wird, normalerweise rshd , oder den Secure Shell-Daemon sshd . Wenn Bash
feststellt, dass es auf diese Weise ausgeführt wird, liest und führt es Befehle
aus ~/.bashrc aus, wenn diese Datei existiert und lesbar ist. Dies wird nicht geschehen, wenn
als sh aufgerufen wird . Der --norc kann verwendet werden, um dieses Verhalten zu unterbinden,
und die --rcfile Option kann verwendet werden, um das Lesen einer anderen Datei zu erzwingen, aber
weder rshd noch sshd generell die Shell mit diesen Optionen aufrufen oder
erlauben, dass sie angegeben werden.

Beispiel

Fügen Sie am Anfang einer entfernten .bashrc Folgendes ein . (Falls .bashrc wird von .profile bezogen oder .bash_profile , deaktivieren Sie dies vorübergehend während des Tests):

echo bashrc
fun()
{
    echo functions work
}

Führen Sie die folgenden Befehle lokal aus:

$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
  • Kein i in $- gibt an, dass die Shell nicht interaktiv ist .
  • Kein führender - in $0 gibt an, dass die Shell keine Anmelde-Shell ist .

Shell-Funktionen, die in der entfernten .bashrc definiert sind kann auch ausgeführt werden:

$ ssh remote_host fun
bashrc
functions work

Mir ist aufgefallen, dass ~/.bashrc ist nur sourced, wenn ein Befehl als Argument für ssh angegeben wird . Das macht Sinn:wenn ssh wird verwendet, um eine reguläre Login-Shell zu starten, .profile oder .bash_profile ausgeführt werden (und .bashrc wird nur bezogen, wenn dies explizit durch eine dieser Dateien erfolgt).

Verwandte:Php:current — Gibt das aktuelle Element in einem Array zurück

Den Hauptvorteil sehe ich darin, .bashrc zu haben Wenn Sie einen (nicht interaktiven) Remote-Befehl ausführen, können Shell-Funktionen ausgeführt werden. Die meisten Befehle sind jedoch in einer typischen .bashrc sind nur in einer interaktiven Shell relevant, z. B. werden Aliase nicht erweitert, es sei denn, die Shell ist interaktiv.

Remote-Dateiübertragungen können fehlschlagen

Dies ist normalerweise kein Problem, wenn rsh oder ssh werden verwendet, um eine interaktive Anmelde-Shell zu starten oder wenn nicht interaktive Shells zum Ausführen von Befehlen verwendet werden. Es kann jedoch ein Problem sein für Programme wie rcp , scp und sftp die Remote-Shells verwenden zum Übertragen von Daten.

Es stellt sich heraus, dass die Standard-Shell des Remote-Benutzers (wie Bash) implizit gestartet wird, wenn scp verwendet wird Befehl. In der Manpage wird dies nicht erwähnt – nur eine Erwähnung, dass scp verwendet ssh für seine Datenübertragung. Das hat zur Folge, dass wenn die .bashrc Befehle enthält, die auf die Standardausgabe gedruckt werden, schlägt die Dateiübertragung fehl , z. B. scp schlägt ohne Fehler fehl.

Warum scp und sftp scheitern

SCP (Secure Copy) und SFTP (Secure File Transfer Protocol) haben ihre eigenen Protokolle für das lokale und entfernte Ende, um Informationen über die zu übertragende(n) Datei(en) auszutauschen. Jeder unerwartete Text von der Gegenseite wird (fälschlicherweise) als Teil des Protokolls interpretiert und die Übertragung schlägt fehl. Laut einer FAQ aus dem Snail Book

Was jedoch häufig passiert, ist, dass Anweisungen entweder in den
System- oder in den Shell-Startdateien pro Benutzer auf dem Server (.bashrc , .profile , /etc/csh.cshrc , .login , etc.), die beim Login Textnachrichten ausgeben,
die von Menschen gelesen werden sollen (wie fortune , echo "Hi there!" usw.).

Dieser Code sollte nur bei interaktiven Anmeldungen eine Ausgabe erzeugen, wenn ein tty vorhanden ist an die Standardeingabe angehängt. Wenn es diesen Test nicht durchführt, fügt es
diese Textnachrichten dort ein, wo sie nicht hingehören:in diesem Fall verunreinigt
der Protokollstrom zwischen scp2 /sftp und sftp-server .

Der Grund, warum die Shell-Startdateien überhaupt relevant sind, ist, dass sshd verwendet die Shell des Benutzers, wenn Programme im Namen des Benutzers gestartet werden (z. B. mit /bin/sh -c „Befehl“). Dies ist eine Unix-Tradition und hat
Vorteile:

  • Die üblichen Einstellungen des Benutzers (Befehlsaliase, Umgebungsvariablen, umask,
    usw.) sind wirksam, wenn Remote-Befehle ausgeführt werden.
  • Die übliche Vorgehensweise, die Shell eines Kontos auf /bin/false zu setzen, um
    sie zu deaktivieren, verhindert, dass der Besitzer Befehle ausführt, falls die Authentifizierung
    aus irgendeinem Grund trotzdem erfolgreich ist.

SCP-Protokolldetails

Für diejenigen, die an den Details der Funktionsweise von SCP interessiert sind, habe ich interessante Informationen in How the SCP protocol works gefunden, die Details zu Running scp with talkative shell profiles on the remote side? enthalten :

Dies kann beispielsweise passieren, wenn Sie dies zu Ihrem Shell-Profil auf dem
Remote-System hinzufügen:

Echo „“

Warum hängt es nur? Das kommt von der Art wie scp in Quelle mode
wartet auf die Bestätigung der ersten Protokollnachricht. Wenn es nicht binär
0 ist, erwartet es, dass es sich um eine Benachrichtigung über ein entferntes Problem handelt, und wartet auf
weitere Zeichen, um eine Fehlermeldung zu bilden, bis die neue Zeile eintrifft. Da
Sie nach der ersten keine weitere neue Zeile gedruckt haben, wird Ihr lokaler scp nur
bleibt in einer Schleife, blockiert bei read(2) . In der Zwischenzeit, nachdem das Shell
-Profil auf der entfernten Seite verarbeitet wurde, scp im Sink-Modus gestartet,
was auch bei read(2) blockiert , warten auf eine binäre Null, die den Beginn
der Datenübertragung anzeigt.

Fazit / TLDR

Die meisten Anweisungen in einer typischen .bashrc sind nur für eine interaktive Shell nützlich – nicht, wenn Remote-Befehle mit rsh ausgeführt werden oder ssh . In den meisten solchen Situationen ist das Setzen von Shell-Variablen, Aliasnamen und das Definieren von Funktionen nicht erwünscht – und das Drucken irgendeines Textes to standard out ist aktiv schädlich, wenn Dateien mit Programmen wie scp übertragen werden oder sftp . Das Beenden nach dem Überprüfen, ob die aktuelle Shell nicht interaktiv ist, ist das sicherste Verhalten für .bashrc .


Linux
  1. Rechnen Sie in der Linux-Shell mit GNU bc

  2. Der Zweck von .bashrc und wie funktioniert es?

  3. Nachdem ich die .bashrc bearbeitet habe, habe ich Geister in der Eingabeaufforderung?

  4. Warum funktioniert die Regex in Bash nur, wenn es sich um eine Variable handelt und nicht direkt?

  5. Warum benötigt der 'bin'-Benutzer eine Login-Shell?

So installieren Sie Fish, die freundliche interaktive Shell, unter Linux

Warum beginnt $shlvl in Nicht-Anmelde-Shells auf Stufe 2, in Anmelde-Shells in Rhel 7 jedoch auf Stufe 1?

Warum wird diese Shell-Pipeline beendet?

Was bedeutet die Syntax |&in der Shell-Sprache?

Warum fügt Strg + V nicht in Bash (Linux-Shell) ein?

Warum funktioniert das Setuid-Bit inkonsistent?