Welcher Shell-Interpreter führt ein Skript ohne Shebang aus?

16

Angenommen, die Standard-Shell für mein Konto ist zsh, aber ich habe das Terminal geöffnet und bash gestartet und ein Skript mit dem Namen ausgeführt prac002.sh. Mit welchem ​​Shell-Interpreter würde das Skript ausgeführt, zsh oder bash? Betrachten Sie das folgende Beispiel:

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % sudo cat /etc/passwd | grep papagolf
[sudo] password for papagolf: 
papagolf:x:1000:1001:Rex,,,:/home/papagolf:/usr/bin/zsh
# papagolf's default shell is zsh

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % bash
# I fired up bash. (See that '%' prompt in zsh changes to '$' prompt, indicating bash.)

papagolf@Sierra:~/My Files/My Programs/Learning/Shell$ ./prac002.sh 
Enter username : Rex
Rex
# Which interpreter did it just use?

** EDIT: ** Hier ist der Inhalt des Skripts

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh 
read -p "Enter username : " uname
echo $uname
7_R3X
quelle
Was sagt das Drehbuch oben?
Michael Homer
@ MichaelHomer: Nichts. Ich habe gerade die Frage bearbeitet, um den Inhalt des Skripts hinzuzufügen. Das Skript verfügt über ausführbare Berechtigungen.
7_R3X
Wenn Sie die aktuelle Shell verwenden möchten, geben Sie wie in die . prac002.shPunktquelle ein, vorausgesetzt, Ihr Skript befindet sich im aktuellen Verzeichnis.
thecarpy
1
Führen Sie es aus . ./prac002.shund es wird mit der aktuellen Shell ausgeführt, d. H. Dot ( .), space () gefolgt vom Pfad Ihres Skripts. Es heißt Punkt-Sourcing Ihres Skripts. ;-)
thecarpy

Antworten:

18

Da das Skript nicht mit einer #!Shebang-Zeile beginnt , die angibt, welcher Interpreter verwendet werden soll, sagt POSIX Folgendes :

Wenn die execl()Funktion aufgrund eines Fehlers fehlschlägt, der dem im Volume System Interfaces von POSIX.1-2008 definierten Fehler [ENOEXEC] entspricht, führt die Shell einen Befehl aus, der dem ersten Aufruf einer Shell mit dem aus der Suche resultierenden Pfadnamen entspricht Operand , wobei alle verbleibenden Argumente an die neue Shell übergeben werden, mit der Ausnahme, dass der Wert von "$ 0" in der neuen Shell auf den Befehlsnamen gesetzt werden kann. Wenn die ausführbare Datei keine Textdatei ist, kann die Shell diese Befehlsausführung umgehen. In diesem Fall wird eine Fehlermeldung geschrieben und der Beendigungsstatus 126 zurückgegeben.

Diese Formulierung ist ein wenig mehrdeutig und verschiedene Muscheln haben unterschiedliche Interpretationen.

In diesem Fall führt Bash das Skript selbstständig aus . Wenn Sie es stattdessen von zsh aus ausführen, würdesh zsh stattdessen verwenden (was auch immer sich auf Ihrem System befindet).

Sie können dieses Verhalten für diesen Fall überprüfen, indem Sie dem Skript die folgenden Zeilen hinzufügen:

echo $BASH_VERSION
echo $ZSH_VERSION

Sie werden feststellen, dass in Bash die erste Zeile Ihre Version ausgibt, während die zweite nie etwas sagt, egal welche Shell Sie verwenden.

  • Wenn Sie zum Beispiel /bin/shsind, gibt dashkeine der Zeilen etwas aus, wenn das Skript von zsh oder dash ausgeführt wird.
  • Wenn /bin/shes sich um einen Link zu Bash handelt, wird in jedem Fall die Ausgabe der ersten Zeile angezeigt.
  • Wenn /bin/shes sich um eine andere Version von Bash handelt als die, die Sie direkt verwendet haben, wird eine andere Ausgabe angezeigt, wenn Sie das Skript von Bash direkt und von zsh aus ausführen.

Der ps -p $$Befehl aus der Antwort von rools zeigt auch nützliche Informationen zu dem Befehl an, den die Shell zum Ausführen des Skripts verwendet hat.

Michael Homer
quelle
Ich habe allerdings upvoted, dass es die DEFAULT SHELL verwendet, wenn kein shebang angegeben ist, unabhängig davon, welche Standard-Shell Sie angegeben haben. Aus diesem Grund sollten Sie immer, imho, einen Shebang angeben.
thecarpy
4
Das ist nicht der eigentliche Sinn der Antwort. Dein zweiter Satz ist sicherlich wahr.
Michael Homer
du hast recht, bash führt es selbst aus, die Standard-Shell auf den meisten meiner Kisten ist .... bash, daher meine Verwirrung. Ich habe gerade unter Solaris 8 mit ksh als Standardshell getestet, klar, bash führt es als bash aus ... habe heute etwas Neues gelernt, danke (upvoted!) ;-)
thecarpy
Vielen Dank. Tritt der Aufruffehler auf execl(), wenn ein Shell-Skript keinen Shebang enthält und als ausgeführt wird scriptname? Kommt es nicht vor, wenn ein Shell-Skript wie folgt ausgeführt wird bash scriptname? Kommt es nicht vor, wenn ein Shell-Skript einen Shebang enthält und als ausgeführt wird scriptname?
Tim
Ich habe einen Beitrag bei unix.stackexchange.com/questions/439376/…
Tim
7

Da es sich bei der Datei nicht um eine vom System erkannte ausführbare Datei handelt und vorausgesetzt, Sie haben die Berechtigung, diese Datei auszuführen, schlägt der execve()Systemaufruf normalerweise mit einem ENOEXEC( nicht ausführbaren ) Fehler fehl .

Was dann passiert, hängt von der Anwendung und / oder der Bibliotheksfunktion ab, mit der der Befehl ausgeführt wird.

Das kann zum Beispiel eine Shell sein, die execlp()/ execvp()libc Funktion.

Die meisten anderen Anwendungen verwenden eine dieser Optionen, wenn sie einen Befehl ausführen. Sie rufen eine Shell zum Beispiel über die system("command line")libc-Funktion auf, die normalerweise shzum Analysieren dieser Befehlszeile (deren Pfad zur Kompilierungszeit festgelegt werden kann (wie bei Solaris im /bin/shVergleich /usr/xpg4/bin/shzu Solaris)) oder zum Aufrufen der $SHELLvon ihnen selbst gespeicherten Shell auf vimit seinem !Befehl oder xterm -e 'command line'vielen anderen Befehlen ( su user -cruft stattdessen die Anmeldeshell des Benutzers auf $SHELL).

Im Allgemeinen wird eine shebang-less-Textdatei, die nicht mit beginnt, #als shSkript betrachtet. Was shes ist, wird jedoch variieren.

execlp()/ execvp(), bei der execve()Rückkehr ENOEXECwird in der Regel darauf aufgerufen sh. Bei Systemen, die mehrere haben, shweil sie mehreren Standards entsprechen können, shwird dies in der Regel zum Zeitpunkt der Kompilierung ermittelt (von der Anwendung unter Verwendung execvp()/ execlp()durch Verknüpfen eines anderen Code-Blocks, der auf einen anderen Pfad verweist sh). Unter Solaris ist dies beispielsweise entweder /usr/xpg4/bin/sh(ein Standard, POSIX sh) oder /bin/sh(die Bourne-Shell (eine veraltete Shell) unter Solaris 10 und höher, ksh93 in Solaris 11).

Wenn es um Muscheln geht, gibt es viele Variationen. bash, AT & T ksh, interpretiert die Bourne-Shell das Skript normalerweise selbst (in einem untergeordneten Prozess, sofern nicht anders angegeben exec), nachdem Sie eine execve(), dh alle nicht exportierten Variablen, zurückgesetzt, alle ausführbaren Dateien geschlossen und alle benutzerdefinierten Traps entfernt haben. Aliase, Funktionen ... ( bashinterpretiert das Skript im shModus). yashwird sich selbst ausführen (mit shas argv[0]so in shmode) um es zu interpretieren.

zsh, pdksh, ashWird -basierte shells typischerweise aufrufen sh(der Pfad , von denen bei der Kompilierung bestimmt).

Wenn für cshund tcsh(und shfür einige frühe BSDs) das erste Zeichen der Datei ist #, werden sie sich selbst ausführen, um sie zu interpretieren, und shansonsten. Das geht auf eine Zeit vor Shebang zurück, in der die Bourne-Shell als Kommentar csherkannt wurde, #aber nicht. Das #war ein Hinweis darauf, dass es sich um ein csh-Skript handelte.

fish(mindestens Version 2.4.0) gibt nur einen Fehler zurück, wenn dieser execve()fehlschlägt (es wird nicht versucht, ihn als Skript zu behandeln).

Einige Shells (wie bashoder AT & T ksh) versuchen zunächst heuristisch festzustellen, ob es sich bei der Datei wahrscheinlich um ein Skript handelt oder nicht. Es kann also vorkommen, dass einige Shells die Ausführung eines Skripts ablehnen, wenn es in den ersten Bytes ein NUL-Zeichen enthält.

Beachten Sie auch, dass execve()einige Shells versuchen, die Shebang-Linie selbst zu interpretieren , wenn ENOEXEC fehlschlägt, die Datei jedoch eine Shebang-Linie enthält.

Also ein paar Beispiele:

  • Wann $SHELList /bin/bash, xterm -e 'myscript with args'wird myscriptvon bashim shModus interpretiert . Während mit xterm -e myscript with args, xtermwird verwendet, execvp()damit das Skript von interpretiert wird sh.
  • su -c myscriptUnter Solaris 10, wo sich rootdie Anmeldeshell befindet /bin/shund /bin/shbefindet, wird die Bourne-Shell myscriptvon der Bourne-Shell interpretiert.
  • /usr/xpg4/bin/awk 'BEGIN{system("myscript")'unter Solaris 10 wird es von /usr/xpg4/bin/sh(dasselbe für /usr/xpg4/bin/env myscript) interpretiert .
  • find . -prune -exec myscript {} \;Unter Solaris 10 (unter Verwendung von execvp()) wird es sogar in einer POSIX-Umgebung (ein Konformitätsfehler) von /bin/sheven with interpretiert /usr/xpg4/bin/find.
  • csh -c myscriptwird interpretiert, cshwenn es mit beginnt #, shsonst mit.

Alles in allem können Sie nicht sicher sein, welche Shell verwendet wird, um das Skript zu interpretieren, wenn Sie nicht wissen, wie und durch was es aufgerufen wird.

In jedem Fall read -pist bash-only die Syntax, daher sollten Sie sicherstellen, dass das Skript von interpretiert wird bash(und diese irreführende .shErweiterung vermeiden ). Entweder kennen Sie den Pfad der bashausführbaren Datei und verwenden:

#! /path/to/bash -
read -p ...

Oder Sie können versuchen, sich auf eine $PATHSuche nach der bashausführbaren Datei zu verlassen (vorausgesetzt, sie bashist installiert), indem Sie Folgendes verwenden:

#! /usr/bin/env bash
read -p ...

( envist fast allgegenwärtig in /usr/bin). Alternativ können Sie es POSIX + Bourne-kompatibel machen. In diesem Fall können Sie es verwenden /bin/sh. Alle Systeme haben eine /bin/sh. Auf den meisten von ihnen wird es (größtenteils) POSIX-kompatibel sein, aber es kann auch vorkommen, dass Sie dort ab und zu eine Bourne-Shell finden.

#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"
Stéphane Chazelas
quelle
1

Wenn Sie keine #!(als shebang bezeichnete ) Leitung haben, wird sh verwendet. Um dies zu überprüfen, können Sie das folgende Skript ausführen.

ps -p $$
echo -n "The real shell is: "
realpath /proc/$$/exe

Auf meinem Computer bekomme ich

  PID TTY          TIME CMD
13718 pts/16   00:00:00 sh
The real program is: /usr/bin/bash

Auch wenn meine Standard-Shell zsh ist . Es benutzt bash, da auf meinem Rechner der Befehl sh von bash implementiert wird .

Scherze
quelle
1
Warum hast du das abgelehnt? Bitte erklären Sie, oder es ist nutzlos ...
Rools
Hasser (stille anonyme Downvoter) werden es hassen. Ich habe aus Ihrer Antwort etwas gelernt. Hier ist eine positive Bewertung. :-)
Mac