FreeBSD Shebang Fehler

Aug 22 2020

Ich möchte Shebang #!/bin/sh -eufo pipefailin mein Drehbuch aufnehmen. Aber es gibt einige seltsame Dinge:

  1. Das Skript würde mit diesem Shebang in FreeBSD fehlschlagen, aber nicht, wenn es unter MacOS ausgeführt wird
  2. Unter FreeBSd funktioniert derselbe Shebang, wenn er (auch /bin/sh) direkt über die Befehlszeile ausgeführt wird .
>>> sh -eufo pipefail -c 'echo hi'  # this works
hi

>>> cat <<EOF > script                                                                                      
#!/bin/sh -eufo pipefail
echo hi
EOF

>>> chmod +x ./script 
>>> ./script  # this doesn't work on FreeBSD but works on MacOS
Illegal option -o ./script

>>> cat ./script 
#!/bin/sh -eufo pipefail
echo hi

>>> uname -a
FreeBS 11.3-RELEASE-p7

Antworten

1 JdeBP Aug 22 2020 at 20:11

MacOS behält das alte FreeBSD-Verhalten von vor 2005 bei. Im Jahr 2005 gab es eine wesentliche Änderung in der Art und Weise, wie der FreeBSD-Kernel #!zu Beginn einer an eine übergebene ausführbare Datei gehandhabten Datei verarbeitet execve()wurde, um sie besser mit einigen anderen Betriebssystemkernen in Einklang zu bringen. einschließlich Linux und des NetBSD-Kernels.

Ein Kommentar im NetBSD-Kernel-Quellcode versucht, dies als universell darzustellen:

* Sammeln Sie das Shell-Argument. alles nach dem Shell-Namen
 * wird als EIN Argument übergeben; das ist das richtige (historische)
 * Verhalten.
- kern/exec-script.c. NetBSD. Zeilen 189 ff.

Ist es eigentlich nicht. Sven Mascheck hat vor etwa einem Jahrzehnt einige Tests durchgeführt, und es gibt vier grundlegende Verhaltensweisen: Das AT & T Unix System 5 hat genauso viel Anspruch auf "korrektes historisches" Verhalten wie das 4.2BSD:

  • Ignorieren Sie die Zeichen (vor 4.2BSD und AT & T Unix System 5).
  • Übergeben Sie die gesamte Zeichenfolge in einem einzigen Argument (4.2BSD, NetBSD, Linux und FreeBSD ab 2005).
  • Teilen Sie die Zeichenfolge durch Leerzeichen auf und übergeben Sie sie als mehrere Argumente (FreeBSD vor 2005 und MacOS).
  • Teilen Sie die Zeichenfolge durch Leerzeichen auf und übergeben Sie nur das erste Argument (AT & T Unix System 5 und Solaris).

Ich habe nur die für diese Antwort relevanten Betriebssysteme in Klammern gesetzt. M. Mascheck überprüfte viel mehr, ebenso wie Ahmon Dancy bei der Diskussion des FreeBSD-Problemberichts 16393. Die vollständigen Listen finden Sie in der weiteren Lektüre.

Was FreeBSD 2005 auf den Kopf stellte, war, dass FreeBSD ironischerweise nicht ganz so einfach war. Es wurde eine Änderung eingeführt, die dazu dienen sollte, dass Dinge, die in populären Büchern über Perl geschrieben wurden, tatsächlich funktionieren: Argumente wurden nach einem Kommentarcharakter übersprungen. Die Bücher hatten Dinge empfohlen wie:

#! / bin / sh - # - * - perl - * -
- Larry Wall, Tom Christiansen und Jon Orwant (2000). Programmierperl: 3. Auflage . O'Reilly Media. ISBN 9780596000271. p. 488.

PR 16393 im Jahr 2000 war eine Möglichkeit, den Kernel dazu zu bringen, ausführbare Perl-Skripte zu verarbeiten, die so geschrieben wurden, wie Larry Wall nicht weniger gesagt hatte, dass sie funktionieren würden. Es hat jedoch andere Sachen kaputt gemacht und nicht vollständig funktioniert.

Es gab ein Hin und Her. Schließlich wurde 2005 der Mechanismus, mit dem die Idee von Larry Wall et al. Zum Funktionieren gebracht wurde, aus dem Kernel entfernt, der sich kompatibel mit Linux, NetBSD und 4.2BSD (anstelle von Solaris und AT & T Unix System 5) verhalten sollte die Verantwortung von sh.

Das Verhalten seit 2005 war daher, dass die Shell drei Argumente erhält, wobei das zweite Argument das gesamte Ende der #!Zeile darstellt und das direkte Aufrufen Ihres Skripts execve()praktisch dasselbe ist wie das Aufrufen von:

sh '-eufo pipefail' ./script

Es sollte ziemlich offensichtlich sein, warum die Almquist-Shell (wie shes bei FreeBSD der Fall ist) denkt, dass dies ./scriptdas Optionsargument für die -oOption ist, und dass sie das pipefailTeil als weitere Einzelbuchstabenoptionen behandelt, die dahinter gesammelt wurden -(was sie nicht hat) noch zur Bearbeitung).

Eine naheliegende Alternative ist, set -o pipefailwie bereits erwähnt, den ersten Befehl im Skript zu habenhttps://unix.stackexchange.com/a/533418/5132für die Bourne Again Shell. Dies wurde jedoch erst 2019 zur FreeBSD Almquist- Shell hinzugefügt und ist daher nur in sehr aktuellen Versionen von FreeBSD verfügbar. (Die Debian Almquist-Shell wurde ab 2020 noch nicht hinzugefügt.)

Weiterführende Literatur

schily Aug 23 2020 at 13:53

Auch wenn Ihre #!Nutzung nicht portabel ist, weil sie mehr als das Einfache verwendet:

#!/bin/sh

oder das allgemein unterstützte Einzelargument wie in:

#!/bin/sh -oneflag

Das eigentliche Problem ist, dass Sie eine nicht tragbare Option verwenden -o pipefail.

Dies ist eine Option von ksh93und bashwird von anderen Shells nicht unterstützt.

Hintergrund:

  • Auf MacOS, /bin/shist , bashdass in einer bestimmten Art und Weise zusammengestellt wurde (zB zu Escape - Sequenzen in macht echoArbeit in der Standardeinstellung) , um es in Übereinstimmung gebracht POSIX. Da Bash unterstützt pipefail(siehe oben), funktioniert dies unter MacOS.

  • Unter FreeBSD /bin/shwird ashdas nicht unterstützt pipefail.

Sie haben zwei Möglichkeiten:

  • Verwende nicht set -o pipefail

  • Warten Sie zwei Jahre und versuchen Sie es erneut. Dies wird höchstwahrscheinlich funktionieren, da wir uns vor 10 Monaten entschlossen haben, diese Option zum nächsten POSIX-Standard (Ausgabe 8) hinzuzufügenhttps://www.austingroupbugs.net/view.php?id=789und da der nächste POSIX-Standard in ca. In einem Jahr besteht eine große Chance, dass FreeBSD set -o pipefailzu ashbald Unterstützung hinzufügt .