Błąd shebang we FreeBSD

Aug 22 2020

Chciałbym umieścić shebang #!/bin/sh -eufo pipefailw moim scenariuszu. Ale jest kilka rzeczy dziwnych:

  1. Skrypt zakończyłby się niepowodzeniem z tym shebangiem we FreeBSD, ale nie działałby w systemie MacOS
  2. we FreeBSd ten sam shebang działa bezpośrednio z wiersza poleceń (również /bin/sh).
>>> 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

Odpowiedzi

1 JdeBP Aug 22 2020 at 20:11

MacOS nadal zachowuje stare zachowanie FreeBSD sprzed 2005 r. W 2005 r. Nastąpiła poważna zmiana w sposobie, w jaki jądro FreeBSD obsługiwało #!na początku przekazywanego pliku wykonywalnego execve(), aby dostosować go bardziej do innych jąder systemu operacyjnego, w tym Linux i jądro NetBSD.

Komentarz w kodzie źródłowym jądra NetBSD próbuje przedstawić to jako uniwersalne:

* zebrać argument powłoki. wszystko po nazwie powłoki
 * jest przekazywany jako JEDEN argument; to jest poprawne (historyczne)
 * zachowanie.
- kern/exec-script.c. NetBSD. wiersze 189 i nast.

W rzeczywistości tak nie jest. Sven Mascheck przeprowadził kilka testów około dziesięć lat temu i istnieją cztery podstawowe zachowania, z których AT&T Unix System 5 ma takie same roszczenia do bycia „poprawnym historycznym” zachowaniem, jak wersja 4.2BSD:

  • Zignoruj ​​znaki (przed 4.2BSD i AT&T Unix System 5).
  • Przekaż cały ciąg w jednym argumencie (4.2BSD, NetBSD, Linux i FreeBSD od 2005).
  • Podziel ciąg na białe znaki i przekaż go jako wiele argumentów (FreeBSD przed 2005 i MacOS).
  • Podziel łańcuch na białe spacje i przekaż tylko pierwszy argument (AT&T Unix System 5 i Solaris)

W nawiasach umieściłem tylko systemy operacyjne związane z tą odpowiedzią. M. Mascheck sprawdził znacznie więcej, podobnie jak Ahmon Dancy podczas dyskusji na temat raportu o problemie FreeBSD 16393. Pełne listy można znaleźć w dalszej części tekstu.

To, co doprowadziło do rozstrzygnięcia sytuacji we FreeBSD w 2005 roku, to fakt, że, jak na ironię, FreeBSD nie było takie proste. Wprowadzono zmianę, która miała sprawić, że rzeczy napisane w popularnych książkach o Perlu faktycznie zadziałają: argumenty zostały pominięte po znaku komentarza. Książki polecały takie rzeczy, jak:

#! / bin / sh - # - * - perl - * -
- Larry Wall, Tom Christiansen, Jon Orwant (2000). Programowanie Perl: 3rd Edition . O'Reilly Media. ISBN 9780596000271. s. 488.

PR 16393 z 2000 roku był sposobem na stworzenie wykonywalnych skryptów Perla obsługujących jądro, napisanych w sposób, który Larry Wall nie mniej powiedział, że będzie działał. Jednak zepsuło to inne rzeczy i nie działało całkowicie.

Było trochę tam iz powrotem na ten temat. Wreszcie, w 2005 roku mechanizm, który sprawił, że pomysł Larry'ego Wall'a i in. Zadziałał, został usunięty z jądra, które zachowywało się kompatybilnie z Linuksem, NetBSD i 4.2BSD (zamiast Solaris i AT&T Unix System 5) i odpowiedzialność sh.

Zachowanie od 2005 roku polega więc na tym, że powłoka pobiera trzy argumenty, z których drugi to cały koniec #!linii, a bezpośrednie wywołanie skryptu execve()jest w rzeczywistości takie samo jak wywołanie:

sh '-eufo pipefail' ./script

Powinno być dość oczywiste, dlaczego powłoka Almquist (która jest tym, co shjest we FreeBSD) myśli, że ./scriptjest argumentem opcji dla -oopcji i że traktuje tę pipefailczęść jako kolejne jednoliterowe opcje zebrane za nią -(których nie ma do przetworzenia jeszcze).

Oczywistą alternatywą jest również użycie set -o pipefailjako pierwszej komendy w skrypcie, jak wskazano whttps://unix.stackexchange.com/a/533418/5132dla powłoki Bourne Again . Zostało to dodane do powłoki FreeBSD Almquist dopiero w 2019 roku, jednak jest dostępne tylko w najnowszych wersjach FreeBSD. ( Powłoka Debian Almquist nie została jeszcze dodana, stan na 2020 r.)

Dalsza lektura

schily Aug 23 2020 at 13:53

Nawet jeśli twoje #!użycie jest nieprzenośne, ponieważ wykorzystuje więcej niż proste:

#!/bin/sh

lub powszechnie obsługiwany pojedynczy argument, jak w:

#!/bin/sh -oneflag

Prawdziwym problemem jest to, że używasz opcji nieprzenośnej -o pipefail.

Jest to opcja od ksh93i bashktóry nie jest obsługiwany przez inne muszli.

Tło:

  • W systemie MacOS /bin/shjest to, bashże zostało skompilowane w określony sposób (np. Aby echodomyślnie działały sekwencje specjalne ), aby był zgodny z POSIX. Ponieważ obsługuje bash pipefail(patrz wyżej), działa to na MacOS.

  • We FreeBSD /bin/shto ashnie obsługuje pipefail.

Masz dwie możliwości:

  • Nie używaj set -o pipefail

  • Poczekaj dwa lata i spróbuj ponownie. Najprawdopodobniej zadziała, ponieważ zdecydowaliśmy się dodać tę opcję do następnego standardu POSIX (Issue-8) 10 miesięcy temu, zobaczhttps://www.austingroupbugs.net/view.php?id=789a ponieważ następny standard POSIX zostanie opublikowany za ok. jeden rok, jest duża szansa, że FreeBSD doda obsługę set -o pipefailna ashszybko.