Обычно я работаю с серверами Ubuntu LTS , который от того, что я понимаю симлинк /bin/sh
к /bin/dash
. Много других дистрибутивов, хотя символическая ссылка /bin/sh
на /bin/bash
.
Исходя из этого я понимаю, что если скрипт использует #!/bin/sh
сверху, он может не работать одинаково на всех серверах?
Существует ли рекомендация, какую оболочку использовать для сценариев, когда требуется максимальная переносимость этих сценариев между серверами?
#!/bin/sh
и не используйте ничего, кроме того, что было в оригинальной оболочке.Ответы:
Существует примерно четыре уровня переносимости сценариев оболочки (что касается строки shebang):
Наиболее переносимый: используйте
#!/bin/sh
шебанг и используйте только базовый синтаксис оболочки, указанный в стандарте POSIX . Это должно работать практически на любой системе POSIX / unix / linux. (Ну, за исключением Solaris 10 и более ранних версий, у которых была настоящая устаревшая оболочка Bourne, предшествующая POSIX, настолько несовместимая, как/bin/sh
.)Второй наиболее переносимый: используйте линию
#!/bin/bash
(или#!/usr/bin/env bash
) shebang и придерживайтесь функций bash v3. Это будет работать в любой системе, в которой есть bash (в ожидаемом месте).Третий наиболее переносимый: используйте
#!/bin/bash
(или#!/usr/bin/env bash
) линию Шебанга и используйте функции bash v4. Это не сработает в любой системе, в которой установлен bash v3 (например, macOS, которая использует его по причинам лицензирования).Наименее переносимый: используйте
#!/bin/sh
shebang и используйте расширения bash для синтаксиса оболочки POSIX. Это не сработает в любой системе, в которой есть что-то, кроме bash для / bin / sh (например, в последних версиях Ubuntu). Никогда не делай этого; это не просто проблема совместимости, это просто неправильно. К сожалению, это ошибка многих людей.Моя рекомендация: используйте самый консервативный из первых трех, который предоставляет все функции оболочки, которые необходимы для сценария. Для максимальной переносимости используйте опцию № 1, но, по моему опыту, некоторые функции bash (например, массивы) достаточно полезны, чтобы перейти к № 2.
Худшее, что вы можете сделать, это # 4, используя неправильный Шебанг. Если вы не уверены, какие функции являются базовыми POSIX, а какие являются расширениями bash, либо придерживайтесь bash shebang (т. Е. Вариант № 2), либо тщательно протестируйте сценарий с помощью очень простой оболочки (например, dash на серверах Ubuntu LTS). В вики Ubuntu есть хороший список ошибок, на которые стоит обратить внимание .
В вопросе Unix & Linux есть очень хорошая информация об истории и различиях между оболочками "Что значит быть совместимым с sh?" и вопрос Stackoverflow "Разница между sh и bash" .
Также имейте в виду, что оболочка не единственная вещь, которая отличается между различными системами; если вы привыкли к linux, вы привыкли к командам GNU, которые имеют много нестандартных расширений, которые вы можете не найти в других системах Unix (например, bsd, macOS). К сожалению, здесь нет простого правила, вам просто нужно знать диапазон изменений для команд, которые вы используете.
Один из самых противных команд с точки зрения переносимости является одним из самых основных:
echo
. Каждый раз, когда вы используете его с какими-либо опциями (например,echo -n
илиecho -e
), или с какими-либо экранированием (обратной косой чертой) в строке для печати, разные версии будут делать разные вещи. Каждый раз, когда вы хотите напечатать строку без перевода строки после нее или с экранированием в строке, используйтеprintf
вместо этого (и узнайте, как это работает - это сложнее, чемecho
есть).ps
Команда также беспорядок .Еще одна общая вещь, которую стоит посмотреть, это недавние расширения / GNUish для синтаксиса опций команды: старый (стандартный) формат команды состоит в том, что за командой следуют опции (с одной чертой, и каждая опция - одна буква), за которой следует командные аргументы. Последние (и часто непереносимые) варианты включают длинные опции (обычно с ними
--
), позволяющие опциям следовать за аргументами и использующие--
для отделения опций от аргументов.источник
sh
), поэтому/bin/sh
не гарантируется, что это правильный путь. В таком случае наиболее переносимым является отсутствие указания какого-либо shebang или его адаптация к используемой ОС.#!/bin/sh
на#!/bin/bash
, сценарий работал отлично. (На мой взгляд, этот сценарий со временем превратился из сценария, которому действительно нужны толькоksh
поведения, а не Bourne. То, что делает большинство дистрибутивов Linux после FHS, это наличие/bin/sh
символической ссылки на оболочку, которую они выбирают для обеспечения совместимости с POSIX, обычноbash
илиdash
.В
./configure
сценарии, который готовит язык TXR к построению, я написал следующий пролог для лучшей переносимости. Сценарий самозагрузится, даже если#!/bin/sh
он не соответствует POSIX старой Bourne Shell. (Я собираю каждую версию на Solaris 10 VM).Идея в том, что мы находим лучшую оболочку, чем та, под которой мы работаем, и повторно выполняем сценарий, используя эту оболочку.
txr_shell
Переменная окружения установлена, так что повторно выполняется скрипт знает , что это рекурсивный экземпляр повторно выполняется.(В моем сценарии эта
txr_shell
переменная также впоследствии используется точно для двух целей: во-первых, она выводится как часть информативного сообщения при выводе скрипта. Во-вторых, она устанавливается какSHELL
переменная вMakefile
, так чтоmake
будет использоваться оболочки тоже для выполнения рецептов.)В системе, где
/bin/sh
есть дефис, вы можете видеть, что вышеуказанная логика найдет/bin/bash
и повторно выполнит скрипт с этим.На боксе Solaris 10
/usr/xpg4/bin/sh
сработает, если Bash не найден.Пролог написан на консервативном диалекте оболочки, использующем
test
для тестов существования файлов, и${@+"$@"}
уловку для расширения аргументов, обслуживающих некоторые сломанные старые оболочки (что было бы просто,"$@"
если бы мы были в соответствующей оболочке POSIX).источник
x
Хакерство не понадобилось бы, если бы использовалось правильное цитирование, так как ситуации, которые предписывали эту идиому, окружают устаревшие вызовы тестов-a
или-o
объединяют несколько тестов.test x$whatever
, что я там выглядит совершая как лук в лаке. Если мы не можем доверять сломанной старой оболочке в цитировании, то последняя${@+"$@"}
попытка бессмысленна.Все варианты языка оболочки Bourne объективно ужасны по сравнению с современными языками сценариев, такими как Perl, Python, Ruby, node.js и даже (возможно) Tcl. Если вам нужно сделать что-то даже немного сложное, в конечном итоге вы будете счастливее, если вместо одного из сценариев оболочки вы воспользуетесь одним из вышеперечисленных.
Единственное преимущество, которое язык оболочки по-прежнему имеет по сравнению с этими новыми языками, заключается в том, что что- то, вызывающее себя
/bin/sh
, гарантированно существует во всем, что подразумевает Unix. Однако это что-то может даже не соответствовать POSIX; многие из устаревших проприетарных Unix-систем заморозили язык, реализованный с/bin/sh
помощью утилит в PATH по умолчанию, до изменений, требуемых Unix95 (да, Unix95, двадцать лет назад и считая). Там может быть набор Unix95 или даже POSIX.1-2001, если вам повезет, инструменты в каталоге не по умолчанию PATH (например/usr/xpg4/bin
), но они не гарантированно существуют.Однако основы Perl более вероятно присутствуют в произвольно выбранной установке Unix, чем Bash. (Под «Основы Perl» Я имею в виду ,
/usr/bin/perl
существует и некоторые , возможно , довольно старая, версия Perl 5, и если вам повезет , множество модулей , которые поставляются с этой версией интерпретатора, также доступны.)Следовательно:
Если вы пишете что-то, что должно работать везде , где подразумевается Unix (например, скрипт «configure»), вам нужно использовать его
#! /bin/sh
, и вам вообще не нужно использовать никаких расширений. В настоящее время я написал бы POSIX.1-2001-совместимую оболочку в этих обстоятельствах, но я был бы готов к исправлению POSIXisms, если бы кто-то попросил поддержки для ржавого железа.Но если вы не пишете что-то, что должно работать везде, то в тот момент, когда у вас возникает соблазн использовать какой-либо Bashism, вам следует остановиться и переписать все это на лучшем языке сценариев. Ваше будущее я будет вам благодарен.
(Поэтому , когда не ассигновать использовать расширение Bash Для первого порядка: никогда Для второго порядка: только для расширения интерактивной среды Bash - например , для обеспечения смарта табуляции завершения и орнаментальные подсказок?.) .
источник
find ... -print0 |while IFS="" read -rd "" file; do ...; done |tar c --null -T -
. Невозможно заставить его работать правильно без bashisms (read -d ""
) и расширений GNU (find -print0
,tar --null
). Переопределение этой строки в Perl будет намного длиннее и неуклюже. Это та ситуация, когда использование bashisms и других расширений, не относящихся к POSIX, является правильным решением.perl -MFile::Find -e 'our @tarcmd = ("tar", "c"); find(sub { return unless ...; ...; push @tarcmd, $File::Find::name }, "."); exec @tarcmd
обратите внимание, что вам не только не нужны никакие ошибки, вам не нужны никакие расширения GNU tar. (Если важна длина командной строки или вы хотите, чтобы сканирование и tar выполнялись параллельно, тогда вам это нужноtar --null -T
.)find
в моем сценарии было возвращено более 50 000 имен файлов, поэтому ваш сценарий может достичь предела длины аргумента. Корректная реализация, которая не использует не-POSIX-расширения, должна будет постепенно наращивать вывод tar или, возможно, использовать Archive :: Tar :: Stream в коде Perl. Конечно, это может быть сделано, но в этом случае Bash-скрипт гораздо быстрее написать, и у него меньше шаблонов, и, следовательно, меньше места для ошибок.find ... -print0 |perl -0ne '... print ...' |tar c --null -T -
. Но вопросы таковы: 1) Я используюfind -print0
и вtar --null
любом случае, так какой смысл избегать bashismread -d ""
, который является точно такой же вещью (расширение GNU для обработки нулевого разделителя, которое, в отличие от Perl, может когда-нибудь стать вещью POSIX )? 2) Perl действительно более распространен, чем Bash? Я недавно использовал образы Docker, и у многих из них есть Bash, но нет Perl. Новые сценарии с большей вероятностью будут запускаться там, чем на древних Unices.