[[Vorlage(Getestet, general)]] {{{#!vorlage Wissen [:Editor: Einen Editor öffnen] [:Terminal: Ein Terminal öffnen] }}} [[Inhaltsverzeichnis()]] [[Bild(Wiki/Icons/terminal.png, 48, align=left)]] Bei [:Shell:Shellskripten] kommt es sporadisch zu Fehlern in Zusammenhang mit dem [wikipedia:Shebang:] und [:ln#Symbolische-Verknuepfungen: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 [:Umgebungsvariable#PATH-erweitern:PATH], mit absolutem oder relativem Pfad, wie z.B.: * {{{#!vorlage befehl script.sh }}} * {{{#!vorlage befehl ~/bin/script.sh }}} * {{{#!vorlage befehl ./bin/script.sh }}} * Explizit mittels Interpreter * {{{#!vorlage befehl bash bin/script.sh }}} * {{{#!vorlage befehl sh ~/bin/script.sh }}} = 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 [http://pubs.opengroup.org/onlinepubs/9699919799/ POSIX-Standard] {en}, 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 {{{#!vorlage befehl 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`. {{{#!vorlage befehl script.sh }}} == Einmal einfach und zurück, und wo war gleich der symbolische Link? == 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 [:man: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 [wikipedia:SUID:]- und [wikipedia:setgid: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 [wikipedia:env:] einheitlich installiert ist, und damit lua im Pfad gefunden wird. == Faustregeln == Soweit nicht anders verordnet, nimmt man {{{#!code bash #!/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: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. = Links = * [:Shell:] {Übersicht} Übersicht * [:Shell/Bash-Skripting-Guide_für_Anfänger:] * [wikipedia_en:Shebang_(Unix):shebang] #tag: Shell