#! / bin / sh vs #! / bin / bash для максимальной переносимости

cherouvim 07/30/2017. 3 answers, 2.886 views
linux bash shell sh

Обычно я работаю с серверами Ubuntu LTS, которые из того, что я понимаю, символические /bin/sh в /bin/dash . Множество других дистрибутивов, хотя symlink /bin/sh to /bin/bash .

Из этого я понимаю, что если скрипт использует #!/bin/sh сверху, он может работать не так же на всех серверах?

Есть ли рекомендуемая практика использования оболочки для скриптов, когда требуется максимальная переносимость этих сценариев между серверами?

1 Comments
Thorbjørn Ravn Andersen 08/01/2017
Между различными раковинами существуют небольшие различия. Если переносимость является наиболее важной для вас, используйте #!/bin/sh и не используйте ничего, кроме исходной оболочки.

3 Answers


Gordon Davisson 08/01/2017.

Существует примерно четыре уровня переносимости для сценариев оболочки (по отношению к линии shebang):

  1. Большинство портативных: используйте #!/bin/sh shebang и используйте only базовый синтаксис оболочки, указанный в стандарте POSIX . Это должно работать практически в любой системе POSIX / unix / linux. (Ну, кроме Solaris 10 и ранее, у которых была настоящая устаревшая оболочка Bourne, предшествующая POSIX, так что она несовместима, как /bin/sh .)

  2. Второй наиболее портативный: используйте строку shebang #!/bin/bash (или #!/usr/bin/env bash ) и придерживайтесь функций bash v3. Это будет работать в любой системе с bash (в ожидаемом месте).

  3. Третий наиболее портативный: используйте строку shebang #!/bin/bash (или #!/usr/bin/env bash ) и используйте функции bash v4. Это произойдет в любой системе с bash v3 (например, macOS, которая должна использовать ее по причинам лицензирования).

  4. Меньше переносной: используйте #!/bin/sh shebang и используйте расширения bash для синтаксиса оболочки POSIX. Это произойдет в любой системе, которая имеет что-то другое, кроме bash для / bin / sh (например, недавние версии Ubuntu). Никогда не делайте этого; это не просто проблема совместимости, это просто неправильно. К сожалению, это большая ошибка.

Моя рекомендация: используйте самые консервативные из первых трех, которые предоставляют все возможности оболочки, которые вам нужны для сценария. Для максимальной переносимости используйте параметр №1, но, по моему опыту, некоторые функции bash (например, массивы) являются достаточно полезными, и я пойду с # 2.

Самое худшее, что вы можете сделать, это # ​​4, используя неправильный shebang. Если вы не знаете, какие функции являются базовыми POSIX и которые являются расширениями bash, либо придерживайтесь bash shebang (например, вариант № 2), либо тщательно протестируйте скрипт с помощью очень простой оболочки (например, на ваших серверах LTS Ubuntu). У вики Ubuntu есть хороший список базизмов, на которые нужно следить .

Есть очень хорошая информация об истории и различиях между оболочками в вопросе Unix & Linux «Что значит быть совместимым с sh?» и вопрос Stackoverflow «Разница между sh и bash» .

Кроме того, имейте в виду, что оболочка - это не единственное, что отличается между разными системами; если вы привыкли к Linux, вы привыкли к командам GNU, которые имеют множество нестандартных расширений, которые вы не можете найти в других системах unix (например, bsd, macOS). К сожалению, здесь нет простого правила, вам просто нужно знать диапазон вариаций для команд, которые вы используете.

Одна из самых противных команд с точки зрения переносимости - одна из самых основных: echo . Каждый раз, когда вы используете его с любыми параметрами (например, echo -n или echo -e ) или с любыми экранами (обратными слэшами) в строке для печати, разные версии будут делать разные вещи. Каждый раз, когда вы хотите напечатать строку без перевода строки после нее или с экранами в строке, используйте вместо этого printf (и узнайте, как это работает - это сложнее, чем echo ). Команда ps также беспорядок .

Еще одна общая вещь для наблюдения - это недавние расширения / GNUish для синтаксиса команды: старый (стандартный) формат команды заключается в том, что за командой следуют опции (с одним тире, а каждый вариант - с одной буквой), а затем аргументы команды. Недавние (и часто не переносимые) варианты включают в себя длинные варианты (обычно вводимые с -- ), позволяющие параметрам приходить после аргументов и использование -- для разделения опций из аргументов.

5 comments
12 pabouk 07/30/2017
Четвертый вариант - это просто неправильная идея. Пожалуйста, не используйте его.
3 Gordon Davisson 07/30/2017
@pabouk Я полностью согласен, поэтому я отредактировал свой ответ, чтобы сделать это более ясным.
1 jlliagre 07/30/2017
Ваше первое заявление несколько вводит в заблуждение. В стандарте POSIX ничего не говорится о том, что внешность shebang говорит, что использование этого приводит к неуказанному поведению. Более того, POSIX не указывает, где должна находиться оболочка posix, только его имя ( sh ), поэтому /bin/sh не гарантируется, что это правильный путь. Самым портативным является то, что вообще не нужно указывать какую-либо shebang, или адаптировать shebang к используемой ОС.
2 Michael Kjörling 07/30/2017
Я недавно попал в №4 с моим сценарием и просто не мог понять, почему он не работает; в конце концов, те же команды работали прочно и делали именно то, что я хотел, чтобы они делали, когда я пробовал их прямо в оболочке. Как только я изменил #!/bin/sh на #!/bin/bash , скрипт работал отлично. (Для моей защиты этот сценарий со временем эволюционировал от того, который действительно нуждался только в sh-ism, к тому, который полагался на поведение, подобное bash).
2 jlliagre 07/30/2017
@ MichaelKjörling. Настоящая оболочка Bourne почти никогда не связана с дистрибутивами Linux и в любом случае не совместима с POSIX. Стандартная оболочка POSIX была создана из поведения ksh , а не Bourne. Большинство дистрибутивов Linux, следующих за FHS, имеют /bin/sh - символическую ссылку на оболочку, которую они выбирают для обеспечения совместимости с POSIX, обычно bash или dash .

Kaz 07/31/2017.

В сценарии ./configure который подготавливает язык TXR для построения, я написал следующий пролог для лучшей переносимости. Скрипт загрузится сам, даже если #!/bin/sh - это несовместимая с POSIX старая оболочка Bourne. (Я создаю каждую версию на Solaris 10 VM).

#!/bin/sh

# use your own variable name instead of txr_shell;
# adjust to taste: search for your favorite shells

if test x$txr_shell = x ; then
  for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
    if test -x $shell ; then
       txr_shell=$shell
       break
    fi
  done
  if test x$txr_shell = x ; then
    echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
    txr_shell=/bin/sh
  fi
  export txr_shell
  exec $txr_shell $0 ${@+"$@"}
fi

# rest of the script here, executing in upgraded shell 

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

(В моем скрипте также txr_shell переменная txr_shell для двух целей: во-первых, она выводится как часть информационного сообщения на выходе скрипта. Во-вторых, он устанавливается как переменная SHELL в Makefile , так что make будет использовать эту оболочку тоже для выполнения рецептов.)

В системе, где /bin/sh является тире, вы можете видеть, что приведенная выше логика найдет /bin/bash и перезапустит сценарий с этим.

В поле Solaris 10 /usr/xpg4/bin/sh будет /usr/xpg4/bin/sh если Bash не будет найден.

Пролог написан на консервативном диалекте оболочки, используя test для тестов существования файла и трюк ${@+"$@"} для расширения аргументов, обслуживающих некоторые сломанные старые оболочки (которые просто были бы "$@" если бы мы были в соответствующей оболочке POSIX).

2 comments
Charles Duffy 07/31/2017
Не нужно было бы хакерства x если бы использовалось правильное цитирование, так как ситуации, которые предусматривали, что идиома окружает теперь устаревшие тестовые вызовы с -a или -o объединяют несколько тестов.
Kaz 07/31/2017
@CharlesDuffy Действительно; test x$whatever я test x$whatever , выглядит как лук в лаке. Если мы не можем доверять сломанной старой оболочке для цитирования, то окончательная попытка ${@+"$@"} бессмысленна.

zwol 07/31/2017.

Все вариации языка оболочки Bourne объективно ужасны по сравнению с современными языками сценариев, такими как Perl, Python, Ruby, node.js и даже (возможно) Tcl. Если вам нужно что-то сделать даже немного сложнее, вы будете более счастливы в долгосрочной перспективе, если вы используете один из вышеперечисленных вместо сценария оболочки.

Единственное преимущество, которое имеет язык оболочки по сравнению с этими более новыми языками, заключается в том, something что- something называющее себя /bin/sh , гарантировано существует на всех, что претендует на Unix. Однако это может быть даже не совместимо с POSIX; многие из устаревших патентов Unixes заморозили язык, реализованный /bin/sh и утилиты в PATH по умолчанию prior изменений, требуемых Unix95 (да, Unix95, двадцать лет назад и подсчета). Может быть набор Unix95 или даже POSIX.1-2001, если вам повезет, инструменты в каталоге, not /usr/xpg4/bin в PATH по умолчанию (например, /usr/xpg4/bin ), но они не гарантируются.

Тем не менее, основы Perl, more likely будут присутствовать на произвольно выбранной установке Unix, чем Bash. (По словам «основы Perl», я имею в виду, что /usr/bin/perl существует и является some , возможно, довольно старой версией Perl 5, и если вам повезло, набор модулей, поставляемых с этой версией интерпретатора, также доступный.)

Следовательно:

Если вы пишете что-то, что должно работать everywhere которое должно быть Unix (например, сценарий «configure»), вам нужно использовать #! /bin/sh #! /bin/sh , и вам не нужно использовать какие-либо расширения. В настоящее время я бы написал POSIX.1-2001-совместимую оболочку в этом случае, но я был бы готов исправить POSIXisms, если бы кто-то попросил поддержки ржавого железа.

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

(Итак, когда целесообразно использовать расширения Bash? Для первого порядка: никогда. Для второго порядка: только для расширения интерактивной среды Bash - например, для обеспечения интеллектуальных вкладок табуляции и приглашений для фантазии.)

Related questions

Hot questions

Language

Popular Tags