Błąd shebang we FreeBSD
Chciałbym umieścić shebang #!/bin/sh -eufo pipefailw moim scenariuszu. Ale jest kilka rzeczy dziwnych:
- Skrypt zakończyłby się niepowodzeniem z tym shebangiem we FreeBSD, ale nie działałby w systemie MacOS
- 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
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
- Wskazuje na część starszej historii: https://unix.stackexchange.com/a/489688/5132
- Sven Mascheck. „ Wyniki testów z różnych systemów ”. Magia, szczegóły na temat mechanizmu shebang / hash-Bang różnych smakach Unix . www.in-ulm.de/~mascheck .
#! - Garance A Drosihn (23.02.2005). Błąd w #! przetwarzanie - jeszcze raz . lista mailingowa freebsd-arch.
- ryand (27.01.2000).
/bin/shnie usuwa komentarzy w linii shebang . Raport o problemie FreeBSD 16393. - Garance A Drosihn (2005). Notatka aktualizacyjna z 28 maja 2005: Zmiana w obsłudze opcji skryptów powłoki . people.freebsd.org/~gad.
sh. Podręcznik ogólnych poleceń BSD . 2019-02-24. freebsd.org.- Wolfram Schneider (12.12.2017). Uzyskaj status zakończenia procesu, który został przesłany potokiem do innego:
set -o pipefailbrakuje dla/bin/sh. Raport o problemie FreeBSD 224270. - Ibrahim Ghazal (30.06.2020). Stan
set -o pipefail. Lista mailingowa powłoki Debian Almquist.
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. Abyechodomyślnie działały sekwencje specjalne ), aby był zgodny z POSIX. Ponieważ obsługuje bashpipefail(patrz wyżej), działa to na MacOS.We FreeBSD
/bin/shtoashnie obsługujepipefail.
Masz dwie możliwości:
Nie używaj
set -o pipefailPoczekaj 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 pipefailnaashszybko.