2010-07-08 6 views
8

Das Szenario ist, dass die Benutzer aufgefordert werden, eine Skriptdatei Quelle:HOWTO: Detect bash von Shell-Skript

$ source envsetup.sh 

Diese Skriptdatei bash nur verfügen verwenden können, so dass wir erkennen die Lauf Shell bash ist oder nicht.

Für andere Shells, die gemeinsame Syntax mit Bash teilen, zum Beispiel, sh, zsh, ksh, möchte ich eine Warnung melden.

Was ist die zuverlässigste Möglichkeit, die aktuelle Shell über Linux, Cygwin, OS X zu erkennen?

Was ich weiß ist $ BASH, aber ich frage mich, die Chancen, dass es scheitern könnte.

Antwort

13

Es gibt eine Reihe von Umgebungsvariablen, die Sie betrachten können, aber viele von ihnen werden nicht erkennen, ob eine andere Shell von bash stammt. Beachten Sie Folgendes:

bash$ echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt" 
SHELL: /bin/bash, shell: , ARGV[0]: -bash, PS1: bash$ , prompt: 

bash$ csh 
[lorien:~] daveshawley% echo "SHELL: $SHELL, shell: $shell, \$0: $0, PS1: $PS1, prompt: $prompt" 
SHELL: /bin/bash, shell: /bin/tcsh, ARGV[0]: csh, PS1: bash$ , prompt: [%m:%c3] %n%# 

[lorien:~] daveshawley% bash -r 
bash$ echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt" 
SHELL: /bin/bash, shell: , ARGV[0]: sh, PS1: bash$ , prompt: 

bash$ zsh 
% echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt" 
SHELL: /bin/bash, shell: , ARGV[0]: zsh, PS1: % , prompt: % 

% ksh 
$ echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt" 
SHELL: /bin/bash, shell: , ARGV[0]: ksh, PS1: bash$ , prompt: 

Es gibt eine Reihe von Variablen, die spezifisch für die verschiedenen Schalen, außer dass sie eine Gewohnheit von Unterschalen vererbt das ist, wo wirklich die Umwelt, was bricht. Das einzige, was fast funktioniert, ist ps -o command -p $$. Dies gibt Ihnen technisch den Befehlsnamen, unter dem die Shell läuft. In den meisten Fällen wird dies funktionieren ... da Anwendungen mit einer Variante des Systemaufrufs exec gestartet werden und der Name des Befehls und der ausführbaren Datei sich unterscheiden können, ist es auch möglich, dass dies fehlschlägt. Bedenken Sie:

bash$ exec -a "-csh" bash 
bash$ echo "$0, $SHELL, $BASH" 
-csh, /bin/bash, /bin/bash 
bash$ ps -o command -p $$ 
COMMAND 
-csh 
bash$ 

Ein weiterer Trick lsof -p $$ | awk '(NR==2) {print $1}' zu verwenden ist. Dies ist wahrscheinlich so nah wie möglich, wenn Sie das Glück haben, lsof handlich zu haben.

2

Die 10 Umgebungsvariable wird Ihnen sagen, welche Login-Shell läuft. Sie können ps $$ verwenden, um die aktuelle Shell zu finden, die verwendet werden kann, wenn Sie wissen möchten, unter welcher Shell das Skript läuft (nicht unbedingt die Login-Shell). Um die ps Ausgabe auf den Shell-Namen zu reduzieren: ps o command= $$ (ich bin mir nicht sicher, wie plattformübergreifend dies funktioniert, funktioniert aber unter Mac OS X).

+0

Probieren Sie dies innerhalb von bash, bevor Sie sich auf $ SHELL verlassen: 'csh -c 'echo" $ SHELL "'' –

+0

Mein Eindruck der Frage ist, dass das Skript die Login-Shell kennen muss. Wenn Sie bash als Login-Shell verwenden, sollte der Befehl bash zurückgeben, was erwartet wird. – Jeff

+0

Nicht die Login-Shell. Wenn Sie sich einloggen, benutzen Sie bash und ändern Sie dann in csh, $ SHELL ist bash, aber "source" würde fehlschlagen. –

10

Dies funktioniert auch

[ -z "$BASH_VERSION" ] && return 
+1

Kann das auch für zsh tun, also 'if [-n" $ {BASH_VERSION} "]; dann ... elif [-n "$ {ZSH_VERSION}"]; dann ... fi' –

+0

habe das Skript: '#!/bin/sh \ echo $ BASH_VERSION' und führe es sogar mit sh oder csh aus einer Bash-Shell:' $ sh./test.sh' und du wirst es bekommen die env var von der vom Benutzer bevorzugten Bash-Version ....total irreführend das laufende Skript – gcb

1

Was hindert Sie daran, tragbaren Schreiben (dh. Shell unabhängig) Skript?

+0

Weil sich upstream sehr stark darauf verlässt. Es ist nicht das, was ich kontrollieren kann ;-) –

+0

Sie patchen also vorhandene Skripte für eine bestimmte Distribution? –

+0

Aus Neugier, welche Bash-spezifische Features können von 'envsetup.sh' verwendet werden? Ich könnte mir vorstellen, dass es nur einige 'Export A = B' Anweisungen enthält ... –

0

Ich würde empfehlen, versuchen, die Anwesenheit der Funktion zu erkennen Sie müssen statt bash vs zsh vs usw. Wenn die Funktion vorhanden ist, es verwenden, wenn keine Alternative. Wenn es keine gute Möglichkeit gibt, die Funktion zu erkennen, außer sie zu benutzen und auf Fehler zu prüfen, dann ist das zwar etwas hässlich, aber ich habe keine bessere Lösung. Was zählt, ist, dass es zuverlässiger funktionieren sollte, als zu versuchen, bash zu erkennen. Und da andere Shells (die sich noch in der Entwicklung befinden) dieses Feature, das früher nur Bash war, irgendwann in der Zukunft haben werden, befasst es sich wirklich mit dem, was wichtig ist, und vermeidet, dass Sie eine Datenbank mit den Versionen jeder Shell führen müssen Unterstützung welche Funktion und welche nicht.

2

Ich denke, dies die praktischste und Cross-Shell

kompatibel wäre
/proc/self/exe --version 2>/dev/null | grep -q 'GNU bash' && USING_BASH=true || USING_BASH=false 

Erläuterung:

/proc/self wird immer nach dem aktuellen Ausführungsprozess zum Beispiel zeigt die folgende Ausführung des pid von readlink es sich von selbst (nicht die Schale, die Readlink ausgeführt)

$ bash -c 'echo "The shell pid = $$"; echo -n "readlink (subprocess) pid = "; readlink /proc/self; echo "And again the running shells pid = $$"' 

Ergebnisse in:

The shell pid = 34233 
readlink (subprocess) pid = 34234 
And again the running shells pid = 34233 

Jetzt: /proc/self/exe ist ein symbolischer Link auf die ausführbare

Beispiel:

bash -c 'echo -n "readlink binary = "; readlink /proc/self/exe; echo -n "shell binary = "; readlink /proc/$$/exe' 

Ergebnisse in:

readlink binary = /bin/readlink 
shell binary = /bin/bash 

Und hier ist die Ergebnisse in Dash und Zsh laufen und Bash thr durch einen Symlink und sogar durch eine Kopie.

[email protected]:~$ cp /bin/bash ./my_bash_copy 
[email protected]:~$ ln -s /bin/bash ./hello_bash 
[email protected]:~$ 

[email protected]:~$ dash -c '/proc/self/exe -c "readlink /proc/$$/exe"; zsh -c "/proc/self/exe --version"; ./hello_bash --version | grep bash; ./my_bash_copy --version | grep bash' 
/bin/dash 
zsh 5.0.7 (x86_64-pc-linux-gnu) 
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu) 
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu) 
[email protected]:~$ dash -c '/proc/self/exe -c "readlink /proc/$$/exe"; zsh -c "/proc/self/exe --version"; ./hello_bash --version | grep bash; ./my_bash_copy --version | grep bash' 
4

Hier ist ein schöner Weg:

if test -z "$(type -p)" ; then echo bash ; else echo sh ; fi 

und Sie können mit, was Sie wollen die „Echo“ Aussagen natürlich ersetzen.

=================

Diskussion:

  • Die $SHELL Variable der ... bevorzugte Shell gibt an Benutzer, die nichts Ihnen sagt, über die Shell, die gerade läuft.

  • Testen $BASH_VERSION ist eine 99% gute Idee, aber es könnte scheitern, wenn irgendein Klugscheißer eine Variable dieses Namens in die SH-Umgebung steckt. Außerdem sagt es Ihnen nicht viel über welche Nicht-Bash-Shell läuft.

  • Die Methode $(type -p) ist superleicht und funktioniert sogar, wenn ein weiser Typ eine Datei namens "-p" in Ihrem $PATH erstellt. Darüber hinaus kann es als Basis für eine 4-Wege-Diskriminierung oder 80% einer 5-Wege-Diskriminierung verwendet werden, wie unten diskutiert.

  • Setzen eines Hash-Bang heißt #! am Anfang des Skripts tut nicht Garantie, dass es dem Dolmetscher Ihrer Wahl zugeführt wird. Zum Beispiel wird meine ~/.xinitrc interpretiert von /bin/sh egal was Hash-Bang (wenn überhaupt) an der Spitze erscheint.

  • Eine gute Übung besteht darin, einige Funktionen zu testen, die in beiden Sprachen zuverlässig existieren, sich aber anders verhalten. Im Gegensatz dazu würde nicht im Allgemeinen sicher sein, die Funktion zu versuchen, die Sie möchten, und zu sehen, ob es fehlschlägt.Wenn Sie zum Beispiel die integrierte Funktion declare verwenden möchten und diese nicht vorhanden ist, könnte sie ein Programm ausführen, und das hat ein unbegrenztes Potenzial für die Abwärtsbewegung.

  • Manchmal ist es vernünftig, kompatiblen Code zu schreiben, indem der Feature-Satz mit dem kleinsten gemeinsamen Nenner verwendet wird ... aber manchmal nicht. Die meisten dieser hinzugefügten Funktionen wurden aus einem bestimmten Grund hinzugefügt. Da diese Interpreten «fast» turing-complete sind, ist es «fast» garantiert möglich, mit dem anderen nachzueifern ... möglich, aber nicht sinnvoll.
  • Es gibt zwei Ebenen der Inkompatibilität: Syntax und Semantik. Zum Beispiel ist die if-then-else-Syntax für csh so anders als bash, dass die einzige Möglichkeit, kompatiblen Code zu schreiben, darin bestehen würde, auf if-then-else-Anweisungen zu verzichten. Das ist möglich, aber es verursacht hohe Kosten. Wenn die Syntax falsch ist, wird das Skript überhaupt nicht ausgeführt. Sobald Sie diese Hürde überwunden haben, gibt es Dutzende von Möglichkeiten, in denen vernünftig aussehender Code zu unterschiedlichen Ergebnissen führt, je nachdem, welcher Dialekt des Interpreters ausgeführt wird.
  • Für ein großes, kompliziertes Programm ist es nicht sinnvoll, zwei Versionen zu schreiben. Schreibe es einmal in der Sprache deiner Wahl. Wenn jemand es unter dem falschen Interpreter startet, können Sie das und einfach den richtigen Interpreter erkennen.

    https://www.av8n.com/computer/shell-dialect-detect

    Es kann unterscheiden:

    • bash
    • bsd-csh
    • Strich
    • ksh93
    • A 5-Wege-Detektor finden Sie hier

    • zsh5

    Weiterhin auf meinem Ubuntu Xenial Box, dass 5-Wege-Kontrolle erstreckt sich auch auf die folgenden:

    • Asche ist ein symbolischer Link zu schleudern
    • csh ist ein symbolischer Link auf/bin/bsd -CSH
    • kSH ist ein symbolischer Link auf/bin/ksh93
    • sh ein Symlink ist
dash