Dieser Artikel ist größtenteils für alle Ubuntu-Versionen gültig.
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:
Der symbolische Link: In /bin gibt es den Link zu sh
, der zur dash weist, früher (oder wenn er explizit abgeändert wurde oder auch in manchen anderen Linuxdistributionen) auch zur bash.
Der verwendete Shebang, das ist die erste Zeile, die einen besonderen Kommentar enthält, wie z.B.:
#!/bin/sh
#!/bin/bash
#!/bin/dash
theoretisch auch etwas wie
#!./splash
Die Form, in der man das Skript aufruft[2]:
Als aufrufbares Programm via PATH, mit absolutem oder relativem Pfad, wie z.B.:
script.sh
~/bin/script.sh
./bin/script.sh
Explizit mittels Interpreter
bash bin/script.sh
sh ~/bin/script.sh
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.
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.
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.
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.
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.
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 19. April 2015 12:35 von aasche erstellt.