Dlaczego zachowanie składni `#!` Nie jest określone przez POSIX?

17

Ze strony Shell Command Language specyfikacji POSIX:

Jeśli pierwszy wiersz pliku poleceń powłoki zaczyna się od znaków „#!”, Wyniki nie są określone.

Dlaczego zachowanie #!nieokreślone przez POSIX? Dziwi mnie, że coś tak przenośnego i powszechnie używanego miałoby nieokreślone zachowanie.

Harold Fischer
źródło
1
Standardy pozostawiają nieokreślone rzeczy, aby nie wiązać implementacji z określonymi zachowaniami. Na przykład „login” to „Nieokreślona czynność, za pomocą której użytkownik uzyskuje dostęp do systemu”.
Kusalananda
2
Ponieważ POSIX nie określa ścieżek wykonywalnych, linia shebang i tak jest z natury nieprzenośna; Nie jestem pewien, czy wiele by to zyskało, określając to niezależnie.
Michael Homer,
1
@MichaelHomer, na pewno nie? Standard może określać, że wiersz zawiera ścieżkę do użycia dla interpretera, nawet bez określania, jaka powinna być ta ścieżka.
ilkkachu
1
@HaroldFischer Z wyjątkiem tego, że nie jest interpretowany przez powłokę, jest interpretowany przez jądro systemu operacyjnego (wykonane przynajmniej w systemie Linux, który może faktycznie wyłączyć tę obsługę w czasie kompilacji), lub jakąkolwiek bibliotekę implementującą tę exec()funkcję. Zatem sprawdzanie w wielu powłokach tak naprawdę nie mówi, jak przenośny jest.
Austin Hemmelgarn,
2
@HaroldFischer Co więcej, nawet wśród systemów operacyjnych zgodnych z POSIX zachowanie nie jest spójne. Linux i macOS zachowują się inaczej: Linux nie tokenizuje linii shebang spacjami. macOS nie pozwala interpreterowi skryptów być innym skryptem. Zobacz także en.wikipedia.org/wiki/Shebang_(Unix)#Portability
jamesdlin

Odpowiedzi:

21

Myślę przede wszystkim dlatego, że:

  • zachowanie różni się znacznie między implementacją. Zobacz szczegóły na https://www.in-ulm.de/~mascheck/various/shebang/ .

    Może jednak teraz określać minimalny podzbiór większości implementacji uniksopodobnych: jak #! *[^ ]+( +[^ ]+)?\n(tylko znaki z przenośnego zestawu znaków w nazwie pliku w tych jednym lub dwóch słowach), gdzie pierwsze słowo jest absolutną ścieżką do natywnego pliku wykonywalnego, rzecz nie jest za długi i zachowanie nieokreślone, jeśli plik wykonywalny jest setuid / setgid, a implementacja określa, czy ścieżka interpretera lub ścieżka skryptu jest przekazywana argv[0]do interpretera.

  • POSIX i tak nie określa ścieżki plików wykonywalnych. Kilka systemów ma narzędzia sprzed POSIX w /bin/ /usr/bini mają narzędzia POSIX gdzie indziej (np. W Solarisie 10, gdzie /bin/shjest powłoka Bourne'a, a POSIX jest w niej /usr/xpg4/bin; Solaris 11 zastąpił ją ksh93, który jest bardziej zgodny z POSIX, ale większość innych narzędzia /binnadal są starożytnymi, innymi niż POSIX). Niektóre systemy nie są systemami POSIX, ale mają tryb / emulację POSIX. Wszystko, czego wymaga POSIX, to istnienie udokumentowanego środowiska, w którym system zachowuje się POSIXly.

    Zobacz na przykład Windows + Cygwin. W rzeczywistości w Windows + Cygwin she-bang jest honorowana, gdy skrypt jest wywoływany przez aplikację cygwin, ale nie przez natywną aplikację Windows.

    Więc nawet jeśli POSIX określił mechanizm shebang, nie można go użyć do pisania skryptów POSIX sh/ sed/ awk... (należy również pamiętać, że mechanizm shebang nie może być użyty do napisania niezawodnego sed/ awkskryptu, ponieważ nie pozwala na przekazanie końca opcji znacznik).

Fakt, że jest nieokreślony, nie oznacza, że ​​nie możesz go użyć (cóż, mówi, że nie powinieneś zaczynać od pierwszego wiersza, #!jeśli spodziewasz się, że będzie to tylko zwykły komentarz, a nie she-bang), ale POSIX nie daje żadnej gwarancji, jeśli to zrobisz.

Z mojego doświadczenia wynika, że ​​używanie shebangs daje większą gwarancję przenośności niż używanie sposobu pisania skryptów powłoki przez POSIX: zrezygnuj z she-bang, napisz skrypt w shskładni POSIX i miej nadzieję, że cokolwiek wywołuje skrypt wywołuje zgodny shz nim POSIX , który jest dobrze, jeśli wiesz, że skrypt zostanie wywołany w odpowiednim środowisku przez odpowiednie narzędzie, ale nie inaczej.

Być może będziesz musiał zrobić takie rzeczy jak:

#! /bin/sh -
if : ^ false; then : fine, POSIX system by default
else
  # cover Solaris 10 or older. ": ^ false" returns false
  # in the Bourne shell as ^ is an alias for | there for
  # compatibility with the Thomson shell.
  PATH=`getconf PATH`:$PATH; export PATH
  exec /usr/xpg4/bin/sh - "$0" ${1+"$@"}
fi
# rest of script

Jeśli chcesz być przenośny na Windows + Cygwin, być może będziesz musiał nazwać swój plik rozszerzeniem .batlub .ps1i użyć podobnej sztuczki dla cmd.exelub powershell.exewywołać cygwin shna tym samym pliku.

Stéphane Chazelas
źródło
Co ciekawe, z wydania 5 : „Konstrukcja #! Jest zarezerwowana dla implementacji, które chcą zapewnić to rozszerzenie. Aplikacja przenośna nie może używać #! Jako pierwszego wiersza skryptu powłoki; nie może być interpretowana jako komentarz”.
muru
@muru Jeśli skrypt byłby naprawdę przenośny, w systemie POSIX z systemem POSIX shnie potrzebowałby linii hashbang, ponieważ byłby wykonywany przez POSIX sh.
Kusalananda
1
@Kusalananda to prawda, jeśli execlplub execvpbyły wykorzystywane, prawda? Gdybym miał użyć execve, spowodowałoby to ENOEXEC?
muru
9

[T] Zachowanie wydaje się spójne między wszystkimi powłokami reklamacyjnymi POSIX. Nie widzę tu potrzeby poruszania się po pokoju.

Nie patrzysz wystarczająco głęboko.

W latach 80. mechanizm ten nie był de facto znormalizowany. Chociaż Dennis Ritchie go wdrożył, wdrożenie to nie dotarło do opinii publicznej po stronie AT&T wszechświata. Był efektywnie tylko publicznie dostępny i znany w BSD; z wykonywalnymi skryptami powłoki niedostępnymi w systemie AT&T Unix. W związku z tym standaryzacja nie była rozsądna. Stan rzeczy jest ilustrowany tym współczesnym doco, jednym z wielu takich:

Zauważ, że BSD pozwala #! interpreterna bezpośrednie uruchamianie plików, a SysV pozwala na bezpośrednie wykonywanie tylko plików a.out. Oznacza to, że wystąpienie jednej z exec…()procedur w programie BSD może wymagać zmiany w SysV, aby /bin/shzamiast tego wykonać interpreter (typlicznie ) dla tego programu.
- Stephen Frede (1988). „Programowanie w systemie X wydanie Y”. Biuletyn Grupy Użytkowników Australian Unix Systems . Tom 9. Liczba 4. p. 111.

Ważnym punktem jest to, że patrzysz na powłoki, podczas gdy istnienie wykonywalnych skryptów powłoki jest w rzeczywistości kwestią exec…()funkcji. To, co robią powłoki, obejmuje prekursory wykonywalnego mechanizmu skryptowego, który do dziś można znaleźć w niektórych powłokach (a także obecnie jest obowiązkowy dla exec…p()podzbioru funkcji) i jest nieco mylący. W tym względzie normą należy się zająć to, jak exec…()działa interpretowany skrypt, a w momencie, gdy POSIX był pierwotnie tworzony , po prostu nie działał przede wszystkim w znacznej części spektrum docelowych systemów operacyjnych .

Podporządkowane pytanie, dlaczego nie zostało to ujednolicone ponieważ, zwłaszcza jako mechanizm magiczna liczba dla tłumaczy skryptów nie osiągnął publicznej wiadomości w stronę AT & T wszechświata i zostało udokumentowane exec…()w definicji systemu 5 interfejsu , na przełomie 1990 :

Plik tłumacza zaczyna się od wiersza formularza

#! nazwa ścieżki [arg]
gdzie pathname jest ścieżką interpretera, a arg jest opcjonalnym argumentem. Gdy masz execplik interpretera, system execjest określonym tłumaczem.
- exec. System V Interfejs Definicja . Tom 1. 1991.

Niestety, zachowanie jest dziś prawie tak bardzo rozbieżne jak w latach 80. i nie ma naprawdę powszechnego zachowania, które można by znormalizować. Niektóre Unices (na przykład HP-UX i FreeBSD) nie obsługują skryptów jako interpretatorów skryptów. To, czy pierwszy wiersz to jeden, dwa lub wiele elementów oddzielonych spacjami, różni się w MacOS (i wersjach FreeBSD przed 2005) i innych. Maksymalna obsługiwana długość ścieżki jest różna. znaki spoza zestawu znaków przenośnej nazwy pliku POSIX są trudne, podobnie jak początkowe i końcowe białe znaki. To, co kończy się argumentem 0, 1 i 2, jest również trudne, ze znacznymi różnicami w zależności od systemu. Niektóre obecnie zgodne z POSIX, ale nie- Systemy Unix nadal nie obsługują żadnego takiego mechanizmu, a nałożenie na niego mandatu spowodowałoby, że przestałyby być zgodne z POSIX.

Dalsza lektura

JdeBP
źródło
1

Jak zauważono w niektórych innych odpowiedziach, implementacje są różne. Utrudnia to standaryzację i zachowanie kompatybilności wstecznej z istniejącymi skryptami. Dotyczy to nawet nowoczesnych systemów POSIX. Na przykład Linux nie w pełni tokenizuje linię shebang spacjami. macOS nie pozwala interpreterowi skryptów być innym skryptem.

Zobacz także http://en.wikipedia.org/wiki/Shebang_(Unix)#Portability

jamesdlin
źródło