checkbashisms-konforme Methode zur Ermittlung der aktuellen Shell

18

In my verwende .profileich den folgenden Code, um sicherzustellen, dass Bash-bezogene Aliase und Funktionen nur bezogen werden, wenn die Anmeldeshell tatsächlich Bash ist :

# 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

Ich bin gerade dabei, meine Shell-Konfigurationsdateien, Skripte und Funktionen der Versionskontrolle zu unterstellen. Ich habe vor kurzem auch damit begonnen, gelegentliche Bashisms aus Shell-Skripten zu entfernen, die nicht von Bash-spezifischen Funktionen profitieren, z . B. Ersetzen function funcname()durch funcname().

Für mein Shell-Datei-Repository habe ich einen Pre-Commit- Hook konfiguriert , der das checkbashismsDienstprogramm aus Debians devscripts-Paket für jede shDatei im Repository ausführt, um sicherzustellen, dass ich nicht versehentlich die Bash-spezifische Syntax einführe. Dies führt jedoch zu einem Fehler für meine .profile:

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

Ich habe mich gefragt, ob es eine Möglichkeit gibt, zu überprüfen, welche Shell ausgeführt wird, die keine Warnung auslöst checkbashisms.

Ich habe die Liste der von POSIX aufgelisteten Shell-bezogenen Variablen überprüft, in der Hoffnung, dass eine davon verwendet werden kann, um die aktuelle Shell anzuzeigen. Ich habe mir auch die Variablen in einer interaktiven Dash-Shell angesehen, aber auch hier keinen geeigneten Kandidaten gefunden.

Im Moment habe ich ausgeschlossen, .profilevon verarbeitet zu werden checkbashisms; Da es sich um eine kleine Datei handelt, ist es nicht schwierig, sie manuell zu überprüfen. Nachdem ich das Problem untersucht habe, möchte ich dennoch wissen, ob es eine POSIX-kompatible Methode gibt, mit der ermittelt werden kann, welche Shell ausgeführt wird (oder zumindest eine Methode, die nicht checkbashismszum Fehlschlagen führt).


Weiterer Hintergrund / Klarstellung

Einer der Gründe, warum ich meine Shell-Konfigurationsdateien der Versionskontrolle unterstelle, ist die regelmäßige Konfiguration meiner Umgebung auf allen Systemen, auf denen ich mich derzeit anmelde: Cygwin, Ubuntu und CentOS (beide 5 und 7, mit Active Directory für Benutzer) Authentifizierung). Ich melde mich am häufigsten über X Windows / Desktop-Umgebungen und SSH für Remote-Hosts an. Ich möchte jedoch, dass dies zukunftssicher ist und möglichst wenig von Systemabhängigkeiten und anderen Tools abhängt.

Ich habe checkbashismsals einfache, automatisierte Überprüfung die Syntax meiner Shell-bezogenen Dateien verwendet. Es ist kein perfektes Tool, z. B. habe ich bereits einen Patch darauf angewendet, damit er sich nicht über die Verwendung command -vin meinen Skripten beschwert . Während der Recherche habe ich erfahren, dass der eigentliche Zweck des Programms darin besteht, die Einhaltung der Debian-Richtlinien sicherzustellen, die meines Wissens eher auf POSIX 2004 als auf 2008 (oder dessen Revision 2013) basieren.

Anthony G - Gerechtigkeit für Monica
quelle
Was Sie tun, ist so POSIX-konform, wie es sein kann, wenn es darum geht, in verschiedenen POSIX-konformen Umgebungen unterschiedlich zu arbeiten. Ihr Rindfleisch ist mit Checkbashisms.
Gilles 'SO - hör auf, böse zu sein'
Übrigens habe ich nie Checkbashisms verwendet, um die Portabilität meiner Konfiguration zu überprüfen. Ich überprüfe es, indem ich es auf verschiedenen Systemen verwende.
Gilles 'SO - hör auf böse zu sein'
2
Übrigens, für das, was Sie hier tun, schreiben Sie ein .bash_profile, das sowohl .profile(bedingt) als auch Quellen enthält .bashrc.
Gilles 'SO - hör auf, böse zu sein'
Danke für das Feedback @Gilles. Das ist eine sehr elegante Lösung für das spezifische Problem.
Anthony G - Gerechtigkeit für Monica

Antworten:

16

Ihre

# 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

Code ist vollständig POSIX-konform und die beste Methode, um zu überprüfen, ob Sie gerade ausgeführt werden bash. Natürlich ist die $BASH_VERSIONVariable bash-spezifisch, aber genau deshalb verwenden Sie sie! Um zu überprüfen, dass Sie laufen bash!

Beachten Sie, dass festgelegt $BASH_VERSIONwird, ob bashals bashoder aufgerufen wird sh. Sobald Sie bestätigt haben, dass Sie ausgeführt werden bash, können Sie [ -o posix ]als Indikator verwenden, dass die Shell aufgerufen wurde als sh(obwohl diese Option auch festgelegt ist, wenn sich POSIXLY_CORRECT in der Umgebung bashbefindet -o posixoder mit oder mit SHELLOPTS = posix in der Umgebung aufgerufen wird. Aber in all diesen Fällen bashwird sich so verhalten, als ob genannt als sh).


Eine andere Variable, die Sie stattdessen verwenden könnten $BASH_VERSIONund die checkbashismsich nicht zu beschweren scheint, es sei denn, Sie haben die -xOption bestanden $BASH. Dies ist auch spezifisch für basheine, mit der Sie auch bestimmen können, ob Sie ausgeführt werden bashoder nicht.


Ich würde auch behaupten, es ist nicht wirklich eine ordnungsgemäße Verwendung checkbashisms. checkbashismsist ein Werkzeug, mit dem Sie portable shSkripte schreiben können (gemäß der shSpezifikation in der Debian-Richtlinie, einer Obermenge von POSIX). Es hilft dabei, nicht standardmäßige Syntax zu identifizieren, die von Leuten eingeführt wird, die Skripte auf Systemen schreiben, auf denen shein Symlink zu ist bash.

A .profilewird von verschiedenen Shells interpretiert, von denen viele nicht POSIX-kompatibel sind. Im Allgemeinen verwenden Sie nicht shals Login - Shell, aber Muscheln mögen zsh, fishoder bashmit erweiterten interaktiven Funktionen.

bashund zsh, wenn sie nicht als aufgerufen werden shund wenn ihre jeweilige Profilsitzungsdatei ( .bash_profile, .zprofile) nicht POSIX-konform ist (insbesondere zsh), aber dennoch gelesen wird .profile.

Es ist also keine POSIX-Syntax, die Sie möchten, .profilesondern eine mit POSIX (für sh) kompatible Syntax , bashund zshfalls Sie jemals diese Shells verwenden möchten (möglicherweise liest sogar Bourne, wie die Bourne-Shell es auch liest, .profileaber auf Linux-basierten Systemen nicht häufig vorkommt) ).

checkbashismsDies würde Ihnen definitiv dabei helfen, Bashismen herauszufinden, könnte jedoch nicht auf eine POSIX-Syntax hinweisen, die mit zshoder nicht kompatibel ist bash.

Wenn Sie hier bash-spezifischen Code verwenden möchten (wie die Umgehung dieses bashFehlers, bei dem keine ~/.bashrcinteraktiven Anmeldeshells eingelesen werden ), ist es besser, dies zu ~/.bash_profiletun (vor oder nach dem Sourcing, ~/.profilein dem Sie Ihre gemeinsame Sitzung abgelegt haben) Initialisierungen).

Stéphane Chazelas
quelle
Das liegt nicht an checkbashismsder verwendeten Variablen.
Thomas Dickey
2
@ThomasDickey, ja, aber das ist ein Fall, in dem der Checkbashisms-Bericht ignoriert werden sollte. Alle anderen bisher genannten Lösungen sind erheblich schlechter.
Stéphane Chazelas
@ StéphaneChazelas Du sagst, dass alle anderen Lösungen schlechter sind. Was wäre falsch an der Verwendung $0für diesen speziellen Fall?
Anthony G - Gerechtigkeit für Monica
2
@AnthonyGeoghegan Das unterscheidet nicht zwischen bash als shund einer anderen Shell als sh.
Gilles 'SO - hör auf böse zu sein'
Es gibt auch ein falsches Positiv, wenn eine dashandere Nicht-Bash-Shell fälschlicherweise mit aufgerufen wurde argv[0] == "bash", was völlig legal ist, wenn es unwahrscheinlich ist.
Kevin
17

Normalerweise verwendet man $0zu diesem Zweck. Auf der von Ihnen verlinkten Seite steht:

0
   (Zero.) Expands to the name of the shell or shell script.
jimmij
quelle
So einfach! Ich habe mich so an $0den Namen eines Shell-Skripts gewöhnt, dass ich vergessen habe, dass es auch auf die aktuelle Shell verweisen kann. Vielen Dank!
Anthony G - Gerechtigkeit für Monica
1
Wie üblich wird diese Konvention von allen gut verhaltenen Programmen eingehalten, aber ein böswilliges Programm kann sich mit Ihnen anlegen, indem es verschiedene Systemaufrufe aus der execFamilie verwendet, da sie es dem aufrufenden Programm ermöglichen, die Binärdatei zu identifizieren, die getrennt von der als Argument zu übergebenden Zeichenfolge ausgeführt werden soll 0.
dmckee
Das funktioniert für die .profile :) cute
Rob
5

Normalerweise gibt die Umgebungsvariable SHELL die Standard-Shell an. Sie müssen die .bashrc-Datei nicht manuell entpacken (nicht wahr, siehe Update unten). Bash sollte dies automatisch tun und sicherstellen, dass sie sich im $ HOME-Verzeichnis befindet.

Die andere Möglichkeit ist, etwas zu tun wie:

 ps -o cmd= $$

Dadurch erfahren Sie, mit welchem ​​Befehl (ohne Argumente, mit = ohne Wert werden keine Spaltenüberschriften angezeigt) der aktuelle Prozess ausgeführt wird. Beispielausgabe:

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

AKTUALISIEREN:

Ich stehe korrigiert! :)

Die .bashrc-Datei wird nicht immer bezogen, wie in den Kommentaren unten und /programming/415403/ angegeben

Sie können also Ihre .bashrc-Datei nach .bash_profile verschieben und prüfen, ob dies funktioniert, ohne den Test durchführen zu müssen. Wenn nicht, haben Sie den obigen Test.

rauben
quelle
1
.bashrcwird eigentlich nicht von einer Login-Shell bezogen sondern .profile(sofern vorhanden) oder .bash_profilesind. SHELLwird von POSIX nicht spezifiziert und leider auch nicht die cmdFormatoption für ps.
Anthony G - Gerechtigkeit für Monica
1
Dies ist, was ich verwenden würde, aber es unterliegt auch böswilligen Manipulationen.
DMCKEE
Beachten Sie, dass ps -o cmd= $$die $SHELLVariable , obwohl sie eigentlich überall funktionieren sollte, völlig irrelevant ist. Dies ist die Standard-Shell des Benutzers und hat keinen Einfluss darauf, welche Shell gerade ausgeführt wird oder welche Shell ein Shell-Skript ausführt.
terdon
2
Nicht wirklich, nein. Viele Shells lesen, ~/.profilewenn sie als Login-Shells gestartet werden. So könnte zum Beispiel dein $SHELLsein cshund du rennst bash -l. Das wird bash als Login-Shell starten, es wird also gelesen ~/.profile, aber dein $SHELLWille zeigt immer noch auf csh. Außerdem wird .profilemöglicherweise explizit ein anderes Skript verwendet. Da das OP ein Höchstmaß an Robustheit anstrebt, sollten alle Arten von Fällen in Betracht gezogen werden. In jedem Fall werden $SHELLkeine nützlichen Informationen zur aktuell ausgeführten Shell bereitgestellt.
Terdon
2
FWIW, auch ich stehe korrigiert da - etwa SHELLnicht von POSIX angegeben zu werden: pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Anthony G - Gerechtigkeit für Monica
4

Die Frage fragt nach der Anmeldeshell des Benutzers sowie nach der aktuellen Shell in einer Weise, die beschwichtigt checkbashisms. Wenn das die Shell sein soll, auf der sich der Benutzer angemeldet hat, würde ich die von /etc/passwdzB

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

Benutzer können natürlich nach dem Anmelden eine neue Shell starten. Wenn eine bash ist und die andere nicht, können Tests der Umgebungsvariablen der bash möglicherweise nicht hilfreich sein.

Einige möchten möglicherweise getenteher als nur die passwdDatei verwenden (aber das würde nicht im Rahmen der Frage liegen).

Basierend auf dem Kommentar zu LDAP und dem Vorschlag für könnte lognamediese alternative Form verwendet werden:

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

Beim Testen ist mir aufgefallen, dass lognamedie Eingabe nicht umgeleitet werden soll (daher habe ich den Ausdruck aufgeteilt gelassen). Eine kurze Überprüfung getentsollte auf den genannten Plattformen funktionieren (obwohl dies in der ursprünglichen Frage vorgesehen sein sollte):

Thomas Dickey
quelle
1
Angenommen, die Benutzerdatenbank befindet sich in / etc / passwd (nicht in LDAP / NIS / mysql ...) und es gibt nur einen Benutzernamen pro Benutzer-ID. (Wäre besser, die erste Spalte gegen zu überprüfen $(logname)).
Stéphane Chazelas
iirc, POSIX definiert das benötigte Dienstprogramm nicht (oder ich hätte es in meiner Antwort verwendet). Es idist eine andere Wahl, ob eine Variable verwendet oder vom Benutzer geändert werden soll.
Thomas Dickey
1
Beachten Sie, dass ich sagte $(logname), nicht $LOGNAME. Die Benutzer-ID und die Anmeldeshell werden beide von dem Benutzernamen abgeleitet, der beim Anmelden verwendet wird. Sie können nur auf Systemen von der Benutzer-ID zur Anmeldeshell zurückkehren, auf denen nur ein Benutzername pro Benutzer-ID vorhanden ist. Oder IOW, der Primärschlüssel in der Benutzerdatenbank ist der Benutzername, nicht die UID.
Stéphane Chazelas
Vielen Dank an @ThomasDickey für eine Antwort aus einer anderen Perspektive. Es ist jedoch etwas komplexer als ich dachte (eher wie die Antworten von Jimmy und Stéphane ). Einer der Gründe, warum ich meine Shell-Konfigurationsdateien der Versionskontrolle unterstelle, ist die regelmäßige Konfiguration meiner Umgebung auf allen Systemen, auf denen ich mich derzeit anmelde: Cygwin, Ubuntu und CentOS (beide 5 und 7, mit Active Directory für Benutzer) Authentifizierung). Aus diesem Grund ziehe ich es vor, die Logik so einfach wie möglich zu halten.
Anthony G - Gerechtigkeit für Monica