Почему поведение синтаксиса `#!` Не определено POSIX?

17

На странице языка команд оболочки спецификации POSIX:

Если первая строка файла команд оболочки начинается с символов «#!», Результаты не указываются.

Почему поведение #!не определено POSIX? Я нахожу странным, что что-то такое портативное и широко используемое будет иметь неопределенное поведение.

Гарольд Фишер
источник
1
Стандарты оставляют вещи неопределенными, чтобы не привязывать реализации к конкретному поведению. Например, «логин» - это «неопределенное действие, с помощью которого пользователь получает доступ к системе».
Кусалананда
2
Поскольку POSIX не указывает пути к исполняемым файлам, строка shebang в любом случае непереносима; Я не уверен, что многое можно было бы получить, указав это независимо.
Майкл Гомер
1
@MichaelHomer, конечно нет? Стандарт может указывать, что строка содержит путь для интерпретатора, даже не указывая, каким должен быть этот путь.
ilkkachu
1
@HaroldFischer За исключением того, что он не интерпретируется оболочкой, он интерпретируется либо ядром ОС (по крайней мере, в Linux, которое может фактически отключить эту поддержку во время сборки), либо любой другой библиотекой, реализующей exec()функцию. Поэтому проверка на наличие нескольких оболочек на самом деле не говорит вам, насколько она портативна.
Остин Хеммельгарн
2
@HaroldFischer Более того, даже среди POSIX-совместимых операционных систем поведение не согласовано. Linux и macOS ведут себя по-разному: Linux не полностью разбивает строку shebang на пробелы. macOS не позволяет интерпретатору сценария быть другим сценарием. Также см en.wikipedia.org/wiki/Shebang_(Unix)#Portability
jamesdlin

Ответы:

21

Я думаю, прежде всего потому, что:

  • поведение сильно варьируется в зависимости от реализации. Смотрите https://www.in-ulm.de/~mascheck/various/shebang/ для всех деталей.

    Однако теперь он может указывать минимальное подмножество большинства реализаций, подобных Unix: например, #! *[^ ]+( +[^ ]+)?\n(если в этих одном или двух словах заданы только символы из переносимого набора символов имени файла), когда первое слово представляет собой абсолютный путь к собственному исполняемому файлу, слишком длинный и поведение не определено, если исполняемый файл setuid / setgid, и реализация определяет, передается ли интерпретатору путь интерпретатора или путь сценария argv[0].

  • POSIX в любом случае не указывает путь к исполняемым файлам. В некоторых системах есть утилиты pre-POSIX в /bin/ /usr/binи утилиты POSIX где-то еще (как в Solaris 10, где /bin/shесть оболочка Bourne, а в POSIX - одна /usr/xpg4/bin; Solaris 11 заменил ее на ksh93, которая более совместима с POSIX, но большинство других инструменты /binвсе еще древние не POSIX). Некоторые системы не POSIX, но имеют режим / эмуляцию POSIX. Все, что требуется POSIX - это наличие документированной среды, в которой система ведет себя POSIXly.

    См. Например, Windows + Cygwin. На самом деле, с Windows + Cygwin, она взволнована, когда сценарий вызывается приложением cygwin, но не собственным приложением Windows.

    Таким образом, даже если в POSIX указан механизм shebang, его нельзя использовать для написания сценариев POSIX sh/ sed/ awk... (также обратите внимание, что механизм shebang нельзя использовать для написания надежного сценария sed/ awkсценария, так как он не позволяет передавать конец опции маркер).

Теперь тот факт, что он не указан, не означает, что вы не можете его использовать (ну, это говорит о том, что первая строка не должна начинаться с того, #!что вы ожидаете, что это будет только обычный комментарий, а не чёртов), но что POSIX не дает вам никакой гарантии, если вы это сделаете.

По моему опыту, использование shebangs дает вам больше гарантий переносимости, чем использование POSIX-сценария написания сценариев оболочки: откажитесь от she-bang, напишите сценарий с shсинтаксисом POSIX и надейтесь, что все, что вызывает сценарий, вызывает для него POSIX-совместимость sh, хорошо, если вы знаете, что скрипт будет вызываться в правильной среде правильным инструментом, но не иначе.

Возможно, вам придется делать такие вещи, как:

#! /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

Если вы хотите быть переносимым на Windows + Cygwin, возможно , придется назвать свой файл с .batили .ps1расширением и использовать некоторый подобный трюк для cmd.exeили powershell.exeвызвать Cygwin shна том же файле.

Стефан Шазелас
источник
Интересно, что из выпуска 5 : «Конструкция #! Зарезервирована для реализаций, желающих предоставить это расширение. Переносимое приложение не может использовать #! В качестве первой строки сценария оболочки; его нельзя интерпретировать как комментарий».
Муру
@muru Если бы скрипт был действительно переносимым, в истинно POSIX-системе, работающей с POSIX sh, ему не понадобилась бы строка hashbang, как это было бы выполнено POSIX sh.
Кусалананда
1
@Kusalananda, это правда только если execlpили execvpбыли использованы, верно? Если бы я использовал execve, это привело бы к ENOEXEC?
Муру
9

[T] его поведение кажется согласованным между всеми оболочками жалоб POSIX. Я не вижу необходимости в комнате для маневра.

Вы не смотрите достаточно глубоко.

Еще в 1980 - х годах, этот механизм был не де - факто стандартизирован. Несмотря на то, что Деннис Ритчи это реализовал, эта реализация не достигла широкой публики в области AT & T во вселенной. Это было эффективно только публично доступно и известно в BSD; с исполняемыми сценариями оболочки, недоступными в AT & T Unix. Таким образом, было не разумно стандартизировать это. Положение вещей иллюстрируется этим современным документом, одним из многих таких:

Обратите внимание, что BSD позволяет #! interpreterнапрямую выполнять файлы, которые начинаются с , в то время как SysV позволяет напрямую выполнять только файлы .out. Это означает, что экземпляр одной из exec…()подпрограмм в программе BSD, возможно, придется изменить в SysV, чтобы /bin/shвместо этого выполнить интерпретатор (типично ) для этой программы.
- Стивен Фреде (1988). "Программирование в System X Release Y". Австралийский Unix Systems User Group Информационный бюллетень . Том 9. Номер 4. с. 111.

Важным моментом здесь является то, что вы смотрите на оболочки, в то время как наличие исполняемых сценариев оболочки на самом деле exec…()зависит от функций. То, что делают оболочки, включает в себя предшественники механизма исполняемых сценариев, которые все еще можно найти в некоторых оболочках даже сегодня (а также в настоящее время обязательные для exec…p()подмножества функций), и это несколько вводит в заблуждение. В этом отношении стандарт должен учитывать то, как exec…()работает интерпретируемый сценарий, и в то время, когда POSIX был изначально создан, он просто не работал в первую очередь в большей части спектра целевых операционных систем .

Подчиненный вопрос, почему это не было стандартизировано , так как, в частности , как механизм магического числа для сценария переводчиков был достигнута общественность в AT & T стороны Вселенной и был задокументирован для exec…()в определении 5 Интерфейса системы , на рубеже 1990 - х лет :

Файл интерпретатора начинается со строки вида

#! путь [arg]
где pathname - это путь интерпретатора, а arg - необязательный аргумент. Когда вы execиспользуете файл интерпретатора, система execиспользует указанный интерпретатор.
- exec. V Интерфейс Определение системы . Том 1. 1991.

К сожалению, поведение остается сегодня почти таким же широко расходящимся, как это было в 1980-х годах, и нет действительно распространенного поведения, которое можно стандартизировать. Некоторые Unices (например, HP-UX и FreeBSD, например) не поддерживают сценарии как интерпретаторы сценариев. То, является ли первая строка одним, двумя или многими элементами, разделенными пробелами, зависит от MacOS (и версий FreeBSD до 2005 года) и других. Максимальная поддерживаемая длина пути варьируется. и символы из набора символов переносимого имени файла POSIX являются хитрыми, как начальные и конечные пробелы. То, чем заканчиваются 0-й, 1-й и 2-й аргументы, тоже сложно, со значительными различиями в разных системах. Некоторые в настоящее время POSIX-совместимые, но не- Системы Unix по-прежнему не поддерживают ни один такой механизм, и его обязательное преобразование приведет к тому, что они перестанут быть POSIX-совместимыми.

дальнейшее чтение

JdeBP
источник
1

Как отмечается в некоторых других ответах, реализации различаются. Это затрудняет стандартизацию и сохранение обратной совместимости с существующими сценариями. Это верно даже для современных систем POSIX. Например, Linux не полностью разбивает строку shebang на пробелы. macOS не позволяет интерпретатору сценария быть другим сценарием.

Также смотрите http://en.wikipedia.org/wiki/Shebang_(Unix)#Portability

jamesdlin
источник