Schreiben von Shell-Skripten, die auf einer beliebigen Shell ausgeführt werden (mit mehreren Shebang-Zeilen?)


19

Ich habe gerade angefangen, mich eingehender mit Shell-Skripten zu befassen, und ich habe mein Skript immer einfach in eine Datei geworfen, markiert chmod +xund dann fertiggestellt /path/to/script.shund den standardmäßig verwendeten Interpreter damit umgehen lassen, was ich für zsh hielt, weil es das ist Ich habe für meine Muschel verwendet. Anscheinend ist dies nur /bin/sheine Standardeinstellung, auch wenn ich das Skript über eine zsh-Eingabeaufforderung ausführe, da ich angefangen habe, zsh-spezifische Inhalte in meine Skripte zu integrieren, und dies schlägt fehl, es sei denn, ich führe es aus zsh /path/to/script.sh.

Um auf den Punkt zu kommen, hier sind meine Fragen:

  1. Welche Shell führt Skripte aus, wenn #!/path/to/shellam Anfang keine shebang line ( ) steht? Ich nehme an /bin/sh, kann es aber nicht bestätigen.
  2. Was wird als "Best Practices" beim Schreiben von Shell-Skripten angesehen, die auf jeder Plattform ausgeführt werden können? (ok, das ist irgendwie offen)
  3. Ist es möglich, ein Skript zu schreiben, das versucht, zsh zu verwenden und auf bash zurückgreift, wenn zsh nicht verfügbar ist? Ich habe versucht, zwei Shebang-Zeilen zu setzen, wie unten, aber es ist nur ein Fehler bad interpreter: /bin/zsh: no such file or directory, wenn ich es auf einer Maschine ohne zsh versuche.

    #!/bin/zsh

    #!/bin/bash

Antworten:


26

Welche Shell führt Skripte aus, wenn zu Beginn keine Shebang-Zeile (#! / Path / to / shell) vorhanden ist? Ich gehe von / bin / sh aus, kann es aber nicht bestätigen.

Der Kernel weigert sich solche Skripte und kehrt zur Ausführung ENOEXEC, so dass das genaue Verhalten auf dem Programm hängt führen Sie ein solches Skript aus .

  • bash 4.2.39 - benutzt sich selbst
  • busybox-ash 1.20.2 - nutzt sich selbst
  • Strich 0.5.7 - läuft / bin / sh
  • fish 1.23.1 - beschwert sich über ENOEXEC und gibt der falschen Datei die Schuld
  • AT & T ksh 93u + 2012.08.01 - nutzt sich selbst
  • mksh R40f - Läuft / bin / sh
  • pdksh 5.2.14 - Läuft / bin / sh
  • sh-heirloom 050706 - nutzt sich selbst
  • tcsh 6.18.01 - Läuft / bin / sh
  • zsh 5.0.0 - Läuft / bin / sh
  • cmd.exe 5.1.2600 - schaut dich komisch an.

Funktioniert in glibcexecv() oder gibt execve()einfach ENOEXEC zurück. Aber execvp()versteckt diesen Fehlercode und ruft automatisch / bin / sh auf. (Dies ist in exec (3p) dokumentiert .)

Was wird als "Best Practices" beim Schreiben von Shell-Skripten angesehen, die auf jeder Plattform ausgeführt werden können? (ok, das ist irgendwie offen)

Halten Sie sich entweder shnur an POSIX-definierte Funktionen, oder gehen Sie einfach zu Bash (was allgemein verfügbar ist) und erwähnen Sie es in Ihren Anforderungen, wenn Sie es verteilen.

(Jetzt, wo ich darüber nachdenke, wäre Perl - oder vielleicht Python - noch portabler, ganz zu schweigen von einer besseren Syntax.)

Füge immer die Shebang-Linie hinzu. Verwenden Sie bei Verwendung von bash oder zsh #!/usr/bin/env bashden Pfad der Shell , anstatt ihn fest zu codieren. (Es ist jedoch garantiert, dass die POSIX-Shell aktiviert ist. /bin/shÜberspringen Sie envin diesem Fall.)

(Leider ist sogar /bin/shnicht immer dasselbe. Das GNU- Autoconf- Programm muss sich mit vielen verschiedenen Macken auseinandersetzen .)

Ist es möglich, ein Skript zu schreiben, das versucht, zsh zu verwenden und auf bash zurückgreift, wenn zsh nicht verfügbar ist? Ich habe versucht, zwei Shebang-Zeilen zu setzen, wie unten, aber es ist nur ein Fehler mit einem schlechten Interpreter: / bin / zsh: Keine solche Datei oder kein solches Verzeichnis , wenn ich es auf einer Maschine ohne zsh versuche.

Es kann nur eine Shebang-Linie geben. Alles, was nach dem Newline-Zeichen steht, wird nicht einmal vom Kernel gelesen und von Shells als Kommentar behandelt.

Es ist möglich , ein Skript zu schreiben, das als ausgeführt wird #!/bin/sh, prüft, welche Shell verfügbar ist und ausgeführt wird, exec zsh "$0" "$@"oder exec bash "$0" "$@"abhängig vom Ergebnis. Die von bash und zsh verwendete Syntax ist jedoch an verschiedenen Stellen so unterschiedlich, dass ich dies aus Gründen der eigenen Vernunft nicht empfehlen würde .


1
Wie haben Sie verfolgt, was jede Shell tatsächlich aufrief, als sie ENOEXEC erhielt?
swrobel

2
@SWrobel: Mit strace -f -e fork,clone,execve. Einige Muscheln wurden /bin/shnach dem Scheitern ausgeführt. andere interpretierten das Drehbuch selbst.
Grawity

1
@SWrobel: Eine andere Methode besteht darin, ein Skript auszuführen, das aus besteht readlink /proc/$$/exe.
Grawity


1
Für cshund tcshhängt das Verhalten davon ab, ob das Skript mit beginnt #(in diesem Fall rufen sie sich selbst anstelle von sh auf, um das Skript zu interpretieren). Das geht auf die Zeit zurück, in der csh Kommentare unterstützte, jedoch nicht die Bourne-Shell. A #war also ein Hinweis darauf, dass es sich um ein csh-Skript handelte.
sch

2

1) Die aktuelle Shell, in der Sie ausgeführt werden. (Welche Shell auch immer)

2) Bleibe beim selben Shell-Typ (bash / dash / ash / csh / was auch immer dein Geschmack ist) und stelle sicher, dass deine "unterstützten Plattformen" die Shell installieren, die du standardmäßig verwenden möchtest. Versuchen Sie außerdem, allgemein verfügbare Befehle auf Systemen zu verwenden. Vermeiden Sie maschinenspezifische Optionen.

3) Es gibt keine wirkliche "Wenn-Dann-Sonst" -Logik für die interpreter directive. Sie sollten eine Shell angeben, die auf allen Systemen vorhanden sein soll, die Sie unterstützen möchten ... #!/bin/bashoder eine generische angeben, #!/bin/shsolange Ihr Skript über alle Shells hinweg ziemlich generisch ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.