Wie erhalte ich * zuverlässig * und * einfach * den aktuellen Shell-Interpreter-Namen?

9

Ich suche nach einer einfachen und zuverlässigen Möglichkeit, den Namen der aktuellen Shell aus einem Skript oder einer Quelldatei ( nicht über die Befehlszeile) abzurufen . Ich hätte erwartet, nur zu tun, $(basename "$SHELL")aber wenn meine Login-Shell ist zshund ich den folgenden Code in some_script.sh habe

this_shell=$( basename "$SHELL" )
echo "The Shell is $this_shell"
echo "The Shell is $0"

und ich führe es mit aus bash some_script.sh, es listet immer noch zshstatt bash, obwohl der verwendete Interpreter ist /bin/bash. Ich bin mir nicht sicher, warum Shell-Designer die Standard-Shell anstelle der aktuellen Shell angezeigt haben, aber das scheint das zu sein, woran wir festhalten.

Es gibt einige andere leicht ähnliche Fragen ( hier und hier ), aber ihre Antworten sind in vielerlei Hinsicht unzureichend.

  1. Sie gehen oft davon aus, dass der Benutzer versucht herauszufinden, welche interaktive Shell er derzeit verwendet, aber das ist verrückt. Ich weiß , was Shell Ich bin Typisierung Befehle in - I - Code benötigen , die überall ausgeführt werden könnte in der Lage sein zu bestimmen , welche Shell es verwendet wird .
  2. Sie geben oft verschiedene Dinge zum Ausprobieren - wieder, als ob Sie sich in der Befehlszeile befinden und nur herumspielen können, bis Sie erfahren, dass Sie Bash verwenden -, aber ich brauche eine einzige Sache, die in allen Kontexten zuverlässig ist.
  3. Sie geben oft Dinge, die aus Skripten völlig nutzlos sind, wie z echo $0. Das schlägt fehl, wie im obigen Skript gezeigt. (Ja, es funktioniert in einer interaktiven Shell-Befehlszeile, aber warum sollten Sie jemals nicht wissen, welche Shell Sie verwenden?)
  4. Sie manchmal geben Befehle, die (in meinen begrenzten Tests) sind die richtigen Informationen, wie ps -p $$, aber nicht über Cross-Plattform, Cross-Shell kompatibel sed / awk - Befehle Rohr durchzukommen nur die Shell Namen und ignorieren die anderen Informationen , die kommen zusammen für die Fahrt.
  5. Dazu gehören Dinge, die nur bei ein paar Muscheln funktionieren, wie $BASH_VERSIONund $ZSH_VERSION. Ich möchte so viele Muscheln wie möglich unterstützen, wie zum Beispiel fish, csh, tcsh.

Wie kann ich zuverlässig und genau erfassen jede aktuelle Shell? Ich suche nach etwas, das plattformübergreifend, in Skripten und für so viele Shells wie möglich und vernünftig funktioniert 1 .

UPDATE : Als ich diese Frage stellte, erwartete ich, dass in die Muscheln eine Einrichtung eingebaut war, um uns diese Informationen zu geben, was anscheinend nicht der Fall ist. Da es jetzt unvermeidlich erscheint, sich auf etwas außerhalb der Muscheln zu verlassen, sollte ich deutlicher machen, dass ich nach einer plattformübergreifenden Lösung frage (die, obwohl sie durch meine Einwände gegen andere Antworten oben impliziert wird, leicht zu übersehen sein könnte, wenn Sie sie nicht anziehen Lesen Sie die Frage nicht sorgfältig durch.

Update 2 Wenn noch jemand glaubt, dass dies ein Duplikat der Nur-Linux-Frage ist, weil Stéphanes Antwort nicht nur Linux ist, sind hier die Unterschiede zwischen dem, was ich verlange, und dem, was er bereitgestellt hat. (Beachten Sie, dass das, was er geschrieben hat, genial ist und ich nicht klopfe, aber es löst mein Problem nicht.)

  1. Ich bin auf der Suche nach etwas einfach und zuverlässig , dass
  2. kann zu jeder Skript- oder Funktionsdefinition (die von einem .zshrcoder .bash_profileoder was auch immer bezogen wird) zum Verzweigen hinzugefügt werden .
  3. Sie können sein Skript nicht als externes Dienstprogramm verwenden, das den Interpreternamen an das aufrufende Skript / die aufrufende Funktion übergibt, da es immer vom Standardinterpreter interpretiert wird und diesen zurückgibt. Dies macht es entweder schwierig oder unmöglich, es für meine Zwecke zu verwenden. Wenn es möglich ist, ist es immer noch sehr, sehr schwierig, und die Lösung dafür ist in der Antwort nicht angegeben. Deshalb hat er meine Frage nicht beantwortet, daher handelt es sich nicht um ein Duplikat.

Wenn Sie etwas sehen möchten, das funktioniert, schauen Sie sich ShellDetective auf GitHub an . Dies könnte es einfacher machen, die Unterschiede zwischen dem, was bereits in SE vorhanden ist, und dem, wonach diese Frage sucht, zu erkennen (und wurde tatsächlich geschrieben, um die Bedürfnisse dieser Frage zu befriedigen, die anderswo nicht erfüllt wurden).


(PS , wenn Sie kann nicht glauben , es ist ein Anwendungsfall für diese, stellen Funktionen , die in Quellen erhalten .zshrc, .bash_profileoder .profileje nachdem , was Server verwendet wird und welche Schalen es zur Verfügung hat. Sie stammen sind , so dass sie keine Shebang - Zeile haben. Sie sind am nützlichsten, wenn sie in einer beliebigen Shell arbeiten können, aber manchmal müssen sie wissen, in welcher Shell sie sich befinden, um zu wissen, wie sie sich verhalten sollen.)


1 Es geht mir nicht um "Muscheln", die keine Muscheln im herkömmlichen Sinne des Wortes sind. Es geht mir nicht um eine Muschel, die ein Typ für sich selbst geschrieben hat. Ich beschäftige mich nur mit echten Shells , die tatsächlich in freier Wildbahn vorkommen und die möglicherweise jemand verwenden muss, wenn er sich bei einem Server anmeldet, auf dem er nicht einfach die gewünschten Shells installieren kann.

Bilderstürmer
quelle
Um die völlig Off-Topic-Diskussion darüber zu lesen, ob Python eine Shell ist: siehe hier
iconoclast
2
Zitat von Gilles ' Antwort auf die Frage Was ist der genaue Unterschied zwischen einem "Terminal", einer "Shell", einem "tty" und einer "Konsole"?   - "Eine Shell ist die primäre Schnittstelle, die Benutzer beim Anmelden sehen, deren Hauptzweck darin besteht, andere Programme zu starten." IMO, das reicht aus, um Perl und Python aus der Kategorie "Shell" zu entfernen.
G-Man sagt 'Reinstate Monica'
Vage verwandt: Kompatibilitätsskripting: Sparen Sie $? zur späteren Verwendung . Ein vorgeschlagener Ansatz: Machen Sie ein Ende der Unterschiede in den Syntaxen der verschiedenen Shells, indem Sie sagen sh -c "…", und erledigen Sie dann den Großteil der Arbeit in der POSIX-Shell-Syntax.
G-Man sagt "Reinstate Monica"
2
Warum willst du das bekommen ? Einige Shells, die als Login-Shells verwendet werden können, haben eine sehr inkompatible Syntax (einige Shells sind Lisp-ähnliche Sprachen!), Daher verstehe ich nicht, warum Sie das tun möchten. Wenn Sie Sourcing-Dateien codieren, müssen Sie mehrere Varianten codieren und Ihren Benutzer anweisen, die entsprechende auszuwählen.
Basile Starynkevitch
1
Meine zweite Frage ist ein Beispiel für die Verkleidung einer Muschel. Was tun Sie, wenn ein Skript von einer ausführbaren Interpreter-Datei ausgeführt wird, deren Name nicht mit der Sprache übereinstimmt, die der Interpreter interpretiert? Wenn ich eine Kopie von csh nehme, benenne ich sie in um fishund verwende sie zum Ausführen Ihres Skripts. Möchten Sie fishoder csh?
Gilles 'SO - hör auf böse zu sein'

Antworten:

5

Ich habe mit dieser Kombination großartige Ergebnisse erzielt:

ps -p $$ | awk '$1 != "PID" {print $(NF)}'

Unter Tru64 (OSF / 1) hat die Ausgabe Klammern um die Shell. Heften Sie an tr -d '()', um sie zu entfernen.

ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()'  

Scheint auf allen Shells unter Solaris 10 und RHEL 5.7 / 6.4 zu funktionieren. Ich habe keine anderen Distributionen wie Debian, Ubuntu oder Mint getestet, aber ich würde denken, dass sie alle gleich funktionieren sollten. Ich habe auch keine Ahnung, ob FreeBSD funktionieren würde.

Geben Sie hier die Bildbeschreibung ein

Chris Embry
quelle
3
Funktioniert nicht in Skripten (gibt stattdessen den Namen des Skripts zurück)
Qw3ry
2

Ich versuche immer noch, dies zu konkretisieren, aber ich denke, ich habe eine Idee, die funktionieren wird. Wie Sie bemerkt haben, ist das, was Sie versuchen, wenn nicht unmöglich, äußerst schwierig in allen Schalen zu tun (jede Variante, die Sie dem Polyglot hinzufügen, erhöht die Komplexität mit einer Geschwindigkeit, die über der linearen Rate liegt). Sie könnten es wahrscheinlich tun, wenn Sie sich in Bourne (sh, ash, dash, bash, ksh, pdksh, zsh usw.) und c-Muscheln (csh, tcsh, fish usw.) aufteilen, aber die Variation zwischen den cshVarianten bietet verschiedene interessante Möglichkeiten Herausforderungen.

Lassen Sie uns also unsere Erkennungsroutine betrügen und in einer bekannten Sprache schreiben (Bash mit einer Shebang-Zeile, C, Perl, Python, was auch immer) und den Namen der ausführbaren Datei des Elternteils bestimmen. Wir können dann zwei Tricks verwenden, um die Informationen an das übergeordnete Element zurückzugeben, nämlich Rückgabewerte und ein einzelnes Wort, das auf Standard out geschrieben wurde. Bei sh absteigenden Shells würden wir 0 zurückgeben und den Namen der Shell schreiben, da alle eine gute Backtick-Handhabung haben. Bei der C-Shells-Familie können wir den Rückgabewert für jeden Satz von Inkompatibilitäten erhöhen, die wir umgehen müssen. Rückgabewerte über zweihundert würden Fehler anzeigen, die mit allem beginnen, was in Ordnung zu sein scheint, aber ich kann einfach nicht sagen, wer meine Eltern bei zweihundert sind.

Das innere Programm scheint unter Linux einfach zu sein ( /proc/ppid/exeist die halbe Miete). Ich bin mir ziemlich sicher, dass dies auf bsd mit ps-Optionen möglich ist, aber ich habe momentan kein laufendes bsd-System zum Testen und mein Mac benötigt eine neue Festplatte (und ist sowieso nur 10.4). Obwohl es die Probleme mit der Shell-Syntax erheblich reduziert, führt es zu einer Reihe anderer Kompatibilitätsprobleme. Ich denke immer noch, dass es Möglichkeiten gibt.

hildred
quelle
Dies ist die Erkenntnis, auf die ich mich schließlich geeinigt habe - dass Sie ein externes Programm benötigen, um die Arbeit zu erledigen -, obwohl ich sie beim Lesen von G-Mans Kommentar erhalten habe sh -c "...", den er einer anderen Frage entnommen hat . (Entschuldigung: Ich habe Ihre Antwort zuerst übersprungen, weil ich keinen Code darin gesehen habe, und bin erst zurückgekommen und habe sie später gelesen.)
iconoclast
In der Frage von @ iconoclast gibt es ein Paradoxon. Er möchte dies von einem Skript aus aufrufen (beachten Sie, dass einige Shells keine Funktionen unterstützen), das von jeder Shell bezogen werden kann, sodass das Skript vermutlich bereits mehrsprachig ist und daher bereits Unterstützung für die Ermittlung der Interpretation benötigt. Nach meiner Erfahrung ist das Schreiben von polyglottem Code (mein which_intepreter ist ein extremes Beispiel, aber siehe dort auch nur für eine Shell) äußerst schwierig und im Allgemeinen die Mühe nicht wert.
Stéphane Chazelas
@ StéphaneChazelas: Ich denke, Sie haben Recht, dass es die Mühe im Allgemeinen nicht wert ist (sicherlich im Allgemeinen nicht und vielleicht immer nicht), aber ich bin nicht bereit aufzugeben ... Ich denke, das Problem gliedert sich wirklich in zwei Probleme: 1. Erhalten der Shell-Name und 2. mit ihm. Beide (zumindest praktisch) benötigen Hilfe von außen, um die Einschränkungen der Muscheln zu vermeiden, die Sie verwenden könnten. Ich habe das erste Problem mit einem kleinen in Crystal geschriebenen Dienstprogramm gelöst, und wenn ich Zeit finde, möchte ich auch das zweite lösen. Übrigens ist Ihre Lösung brillant, aber offensichtlich sehr komplex.
Bilderstürmer
@iconoclas: Unter der Annahme, dass Sie den Shell-Namen zuverlässig abrufen können (beachten Sie, dass die Befehlssubstitution und die Syntax der Variablenzuweisung zwischen den Shells variieren), können Sie keine Aktionen wie case $shell in sh)...(das ist nur Bourne-Syntax) oder switch ($shell)(nur csh) ausführen. test "$shell" = "sh" && do-somethingarbeitet in csh/ sh/ rc/ es, aber nicht fish...
Stéphane Chazelas
Ja, ich bin schon darauf gestoßen. Das Schlimmste ist, dass fish nicht einmal ein Skript lädt, dessen Syntax ihm nicht gefällt. Sie können also nicht einfach STDERR an senden /dev/null. Aber ich habe eine Lösung, die ich veröffentlichen werde, sobald alles funktioniert.
Bilderstürmer
1

versuche so etwas

shell_bin=$(ps h -p $$ -o args='' | cut -f1 -d' ')
echo $shell_bin
Dany Flores
quelle
Dies schlägt in cshund fehl tcsh.
Bilderstürmer
mit einer kleinen modifikation wird es funktionieren fish, aber dann fishleider nur in : ps h -p %self -o args='' | cut -f1 -d' '. Dies schafft ein Henne-Ei-Problem ... Sie müssen wissen, dass Sie fishin sind, um die fishVersion des Codes auszuführen ...
iconoclast
@ iconoclast- set shell_bin=`ps -p $$ -o args='' | cut -f1 -d' '`- wird mit CSH arbeiten, aber nicht mit den anderen
DarkHeart
@DarkHeart: tatsächlich , das nicht zu, da die variable Zuordnung nicht das Problem auf war csh/ tcshwar es dieser Teil: ps -p $$ -o args='' | cut -f1 -d' ', die Renditen -shauf cshund -cshauf tcsh.
Bilderstürmer
1

Ich glaube, Sie können nicht immer den Namen der aktuellen Shell erhalten , und ich denke, Sie sollten sich der Einschränkungen dessen bewusst sein, was möglich ist.

Bei Linux-Distributionen haben die meisten Benutzer bash ihre Login- und interaktive Shell (da dies bashbei den meisten Distributionen die Standard-Shell ist). Einige Benutzer würden setzen ihre Schale zsh, csh(und Varianten) oder fish.

(Wie andere Kommentare und Antworten erklären, finden zuverlässig die Schale aus bash, zsh, tcsh, fishist schon eine Herausforderung)

Aber einige seltsame Benutzer ihre Login - Shell zu etwas ganz anderes gesetzt werden - ein Lisp - Interpreter scsh, eseinige Skriptsprache à la Python oder Ocaml oder Perl, etc ...- und es ist ihre Freiheit , dies zu tun. Wahrscheinlich codieren einige Leute ihre eigene Shell und verwenden sie interaktiv. Selbst wenn Sie ihre seltsame Shell gefunden haben, können Sie nichts Nützliches daraus machen (daher sollten Sie meiner Meinung nach nicht versuchen, den Namen der Shell zu ermitteln).

Ich vermute also, dass Sie eine beschaffbare Datei codieren (möglicherweise eine generieren), um eine Software zu konfigurieren. Erklären Sie einfach, was Sie tun, und codieren Sie den allgemeinen Fall von bash(und vielleicht zsh& tcsh) ....

Basile Starynkevitch
quelle
-2

Verwendung unten auf eigenen Risiko Haftungsausschluss

tmp=`head -n 1 $0`
SHELL_BIN=`readlink -f ${tmp#*!}`
SHELL=${SHELL_BIN##*/}
Gwillie
quelle
Es gibt mir eine leere Zeile ... aber wenn ich verstehe, was Sie versuchen, ist es sehr klug
Bilderstürmer
Es sollte keine leere Zeile geben, es sei denn, die -fOption in readlink, einige Implementierungen haben diesen Schalter nicht
gwillie
Ich bin unter OS X und die Manpage listet -fals verfügbare Option auf, wobei ich das formatals Argument nehme .
Bilderstürmer
Dies kann in einigen besonderen Fällen funktionieren, wenn $ 0 ein Skript ist (beginnt mit #! / ...), dies ist jedoch nicht das übliche Setup. Hier zeigen die meisten $ 0 auf eine ELF-Datei.
readlinkIch arbeite nicht an meinem Puredarwin VM, also habe ich keine Ahnung, was dort los ist.
Gwillie