staging.inyokaproject.org

Shebang für Shellskripte

Dieser Artikel wurde für die folgenden Ubuntu-Versionen getestet:

Dieser Artikel ist größtenteils für alle Ubuntu-Versionen gültig.

Zum Verständnis dieses Artikels sind folgende Seiten hilfreich:

Wiki/Icons/terminal.png Bei Shellskripten kommt es sporadisch zu Fehlern in Zusammenhang mit dem Shebang und symbolischen Links. Vor allem die Ursache des Fehlers will dieser Artikel erklären.

Das Szenario besteht aus drei Einflussgrößen:

Komplexität reduzieren

Für sich genommen sind die Punkte leicht zu verstehen - alleine, wenn sie sich zu einer Einheit verknüllen, wird die Frage doch ein wenig komplex. Um die Komplexität anzugehen, sollte man die Konzepte isoliert voneinander verstehen - das ist einfach. Danach ist es auch nicht schwer, die Kombination der Effekte zu verstehen.

Interpreter

Ein Skript kann von unterschiedlichen Interpretern interpretiert werden - oder auch nicht. Es gibt einen POSIX-Standard 🇬🇧, den die populären Shells alle ganz gut beherrschen. Was darüber hinausgeht kann die eine Shell aber die andere nicht, und die andere kann jenes, was erstere nicht kann oder anders löst.

Wenn man einen Interpreter ausdrücklich aufruft, dann wird dieser verwendet - ohne wenn und aber; der Shebang schaut in die Röhre, wird also ignoriert. Natürlich kann man nicht erzwingen, dass das Skript in einem Interpreter funktionieren wird, für den es nicht geschrieben wurde. Das Skript wird ja nicht auf magische Weise umgeschrieben.

Der Aufruf

Wenn man den Interpreter explizit aufruft, also beispielsweise

dash script.sh 

dann muss man zuvor das Skript nicht ausführbar machen, weil man den Interpreter ausführt, und das Skript dessen Eingabedatei ist, so wie z.B. ein Bild die Eingabe für ein Grafikprogramm wie Gimp ist.

Lässt man den Interpreter weg, muss man das Skript erst ausführbar machen; dann wird der Shebang ausgewertet, der in der ersten Zeile steht und aus einem Kommentarzeichen # (engl.: sharp) besteht, dem ! (engl.: bang) und dem Pfad zum Interpreter - üblicherweise dem absoluten Pfad, also /bin/bash oder /bin/sh, insgesamt also #!/bin/bash.

script.sh 

Das war ja jetzt alles nicht so schwer - wo ist das Problem?

Zurück zur Komplexität: Jetzt steht im Skript als Shebang #!/bin/sh, aber im Verzeichnis /bin ist sh ein symbolischer Link der auf dash weist, oder in früheren Zeiten auf die bash. Dann kann man doch auch gleich #!/bin/dash schreiben, das ist doch dann gleichbedeutend?

  • Ja, ist es, wenn das Skript in einer unterstützten Standard-Ubuntu-Installation verwendet wird.

  • Ja, ist es auch sonst, wenn /bin/sh eine symbolische Verknüpfung auf /bin/dash ist.

  • Nein, ist es nicht, wenn /bin/sh beispielsweise eine Verknüpfung auf /bin/bash ist. Die Shell kann untersuchen, wie sie aufgerufen wurde, und sie tut das auch - zumindest die bash macht es. Sie stellt fest, dass sie als sh aufgerufen wurde, und verhält sich so, als habe man die Option --posix verwendet - daher ist auch der Aufruf #!/bin/sh nicht gleichbedeutend zum Aufruf #!/bin/bash (Genaueres in der manpage der Bash auf den ersten Seiten).

So, das hat die Sache etwas subtiler gemacht, und wer sich davon angeregt fühlt, der schreibt in die eigenen Skripte auch noch Weichen hinein, die unterscheiden, ob das Skript mit vollem Pfad oder relativem Pfad aufgerufen wurde.

Mehr Komplexität gibt es aber hier nicht - das war schon alles.

Was soll das alles - wozu der Wildwuchs?

Es gibt im wesentlichen drei Gründe, sich für die Shell zu interessieren:

  • Man schreibt Konfigurationsskripte und ähnliches, die auf ganz unterschiedlichen Systemen laufen sollen. Dann ist man an einer großen Portabilität interessiert, und wird versuchen, sich auf den POSIX-Standard zu beschränken - den größten, gemeinsamen Nenner. Dann verwendet man auch den Shebang #!/bin/sh

  • Man schreibt Skripte für ein Spezialgerät, bei dem es sehr auf die Größe der Shell ankommt. Dann wird man vielleicht die dash der bash vorziehen, aber je nach Gerät mag es andere ggf. noch kleinere Shells geben.

  • Man möchte möglichst viele, fortgeschrittene Konzepte einer bestimmten Shell verwenden. Dann wird man wohl zu etwas wie bash oder zsh greifen, und den entsprechenden Shebang verwenden.

Sonstiges

  • Bei Skripten mit Shebang werden vom Linuxkernel die SUID- und SGID-Flags ignoriert.

  • Mittels Shebang kann man ein Skript aufrufen, welches seinerseits wieder einen Shebang hat usw. Allerdings nur vier Rekursionstiefen tief, dann ist Schluss.

  • Der Shebang kann um Parameter erweitert werden:

    #!/bin/bash -f  
  • Skripte in anderen Sprachen wie Lua, Python, Ruby usw. lassen sich analog mit einem Shebang starten - sofern das # dort als Kommentar toleriert wird, also

    #!/usr/bin/python
  • Bei Skripten, bei denen man nicht weiß, auf welcher Unix-Plattform sie landen werden, findet man auch einen zweistufigen Aufruf, weil man beispielsweise nicht weiß, wo Lua installiert ist. Man ruft dann

    #!/usr/bin/env lua 

    auf, und verlässt sich darauf, dass env einheitlich installiert ist, und damit lua im Pfad gefunden wird.

Faustregeln

Soweit nicht anders verordnet, nimmt man

1
#!/bin/bash

als erste Zeile des Skripts.

  • Was mit der dash klappt und mit sh, das klappt fast immer auch mit der bash - umgekehrt aber nicht.

  • Die bash ist weit verbreitet und kommt auch bei Ubuntu als interaktive Shell zum Einsatz.

  • Die bash ist viel mächtiger als dash. Die Zeit, die man beim Schreiben spart holt die schnellere Startzeit der Dash niemals rein - außer man arbeitet in einer Umgebung, die es dringend gebietet mehr als Faustregeln zu lernen.

  • Die zsh ist auch sehr mächtig, aber Leute, die zsh-Tipps geben, wissen, dass kaum jemand die zsh nutzt. Deswegen schreiben sie dann auch ausdrücklich dazu, dass es zsh-Code ist.

Diese Revision wurde am 30. Januar 2018 10:18 von Heinrich_Schwietering erstellt.
Die folgenden Schlagworte wurden dem Artikel zugewiesen: Shell