Существует ли минимально совместимая с POSIX.2 оболочка?

Существует ли минимальная POSIX.2 совместимая оболочка (пусть ее называют mpcsh) в следующем смысле:

if mpcsh myscript.sh ведет себя корректно в моей (совместимой) системе, тогда xsh myscript.sh будет вести себя одинаково для любой совместимой с POSIX2 оболочки xsh в любой совместимой системе. ( "Идентично" до менее важных вещей, таких как формулировка сообщений об ошибках и т.д.)

Подходит ли тире?

Если нет, есть ли способ проверить соответствие myscript.sh?

Ответы

Ответ 1

Печальный ответ заранее

Это не поможет вам (не так много и надежно, как вы ожидали бы, и так или иначе захотите).


Вот почему.

Одна большая проблема, которая не может быть решена виртуальной "оболочкой POSIX", - это вещи, которые неоднозначно сформулированы или просто не рассматриваются в стандарте, так что оболочки могут реализовывать вещи по-разному, все еще придерживаясь стандарта.

Возьмем два примера, касающиеся трубопроводов, первый из которых хорошо известен:

Пример 1 - обзор

$ ksh -c 'printf "foo" | read s; echo "[${s}]"'
[foo]

$ bash -c 'printf "foo" | read s; echo "[${s}]"'
[]

ksh выполняет последнюю команду канала в текущей оболочке, тогда как bash выполняет все - включая последнюю команду - в подоболочке. bash 4 представил параметр lastpipe, который заставляет его вести себя как ksh:

$ bash -c 'shopt -s lastpipe; printf "foo" | read s; echo "[${s}]"'
[foo]

Все это (дебативно) согласно стандарт:

Кроме того, каждая команда конвейера с несколькими командами находится в среде подсетей; однако в качестве расширения любые или все команды в конвейере могут выполняться в текущей среде.

Я не на 100% уверен в том, что они имели в виду с расширением, но на основе других примеров в документе это не означает, что оболочка должна обеспечить способ переключения между поведением, а просто, что она может, если она этого пожелает, реализовать вещи в этом "расширенном виде". Другие люди читают это по-другому и спорят о том, что поведение ksh не соответствует стандартам и я понимаю, почему. Мало того, что формулировка не повезла, не рекомендуется это допускать в первую очередь.

На практике не имеет никакого значения, какое поведение является правильным, так как это "два больших оболочки "" ", и люди подумают, что если вы не используете их расширения и только предположительно POSIX-совместимый код, будет работать в любом случае, но правда в том, что если вы полагаетесь на одно или другое поведение, упомянутое выше, ваш script может разрушиться ужасно.

Пример 2 - перенаправление

Об этом я узнал всего пару дней назад, см. мой ответ здесь:

foo | bar 2>./qux | quux

Здравый смысл и POLA сообщает мне, что, когда поражается следующая строка кода, оба quux и bar должны были закончить, что означает, что файл ./qux полностью заполнен. Правильно? Нет.

POSIX утверждает, что

Если конвейер не находится в фоновом режиме (см. Асинхронные списки), оболочка должна дождаться завершения последней команды, указанной в конвейере, и может также дождаться завершения всех команд.)

Май (!) дождитесь завершения всех команд! WTH!

ждет:

Оболочка ожидает завершения всех команд в конвейере перед возвратом значения.

но :

Каждая команда, за исключением, возможно, последней, запускается как отдельный процесс; оболочка ждет завершения последней команды.

Итак, если вы используете перенаправление между каналом, убедитесь, что знаете, что вы делаете, поскольку это обрабатывается по-разному и может ужасно ломаться по крайним случаям, в зависимости от вашего кода.

Я мог бы привести еще один пример, не связанный с конвейерами, но я надеюсь, что этих двух достаточно.

Заключение

Наличие стандарта хорош, постоянно пересматривая его еще лучше, и придерживаться его отлично. Но если стандарт выходит из строя из-за двусмысленности или вседозволенности, все может неожиданно сломаться, фактически оказывая полезность стандартной пустоты.

На практике это означает, что в дополнение к написанию "POSIX-совместимого" кода вам все равно нужно думать и знать, что вы делаете, чтобы предотвратить определенные события.

Все, что сказано, одна оболочка, которая еще не упоминалась, posh, которая предположительно представляет собой POSIX плюс еще меньшее количество расширений, чем dash имеет (в основном echo -n и ключевое слово local) в соответствии с его man-страницей:

BUGS
   Any bugs in posh should be reported via the Debian BTS.
   Legitimate bugs are inconsistencies between manpage and behavior,
   and inconsistencies between behavior and Debian policy
   (currently SUSv3 compliance with the following exceptions:
   echo -n, binary -a and -o to test, local scoping).

YMMV.

Ответ 2

В настоящее время нет единственной модели подстановки для POSIX shell.

С оригинальной оболочки Bourne оболочка POSIX приняла ряд дополнительных функций.

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

Например, POSIX допускает арифметические выражения в формате:

var=$(( expression ))

но он не позволяет эквивалент:

(( var = expression ))

поддерживается bash и ksh93.

Я знаю, что bash имеет параметр set -o posix, но это не отключит никаких расширений.

$ set -o posix
$ (( a = 1 + 1 ))
$ echo $a
2

Насколько мне известно, ksh93 пытается соответствовать POSIX из коробки, но все же допускает расширения.

Ответ 3

Если нет, есть ли способ проверить соответствие myscript.sh?

Это в основном случай обеспечения качества. Начните с:

  • обзор кода
  • модульные тесты (да, я это сделал)
  • функциональные тесты
  • выполните набор тестов с таким количеством различных программ оболочки, которые вы можете найти. (ash, bash, dash, ksh93, mksh, zsh)

Лично я нацелен на общий набор расширений, поддерживаемый bash и ksh93. Они являются самыми старыми и наиболее доступными интерпретаторами языка оболочки.

EDIT Недавно я столкнулся с rylnd/shpec - структурой тестирования для вашего кода оболочки. Вы можете описать функции своего кода в тестовых случаях и указать, как их можно проверить. Раскрытие информации: я помог сделать работу с ней через bash, ksh и тире.