Правильно ли использовать / bin / sh в hashbang, если оболочка Bourne недоступна в дистрибутиве?

15

Как правило, сценарии оболочки содержат следующий комментарий в первой строке файла сценария: #!/bin/sh. Согласно проведенным мною исследованиям это называется «хэш-бэнг», и это обычный комментарий. Этот комментарий сообщает Unix, что этот файл выполняется Bourne Shell в каталоге /bin.

Мой вопрос начинается с этого момента. До сих пор я не видел этот комментарий, как #!/bin/bash. Это всегда #!/bin/sh. Однако в дистрибутивах Ubuntu нет программы Bourne Shell. У них есть Bourne Again Shell (Баш).

С этой точки зрения, правильно ли размещать комментарий #!/bin/shв сценариях оболочки, написанных в дистрибутивах Ubuntu?

Goktug
источник
1
Смотрите мой ответ на этот вопрос об ошибке сервера: #! / Bin / sh vs #! / Bin / bash для максимальной переносимости .
Гордон Дэвиссон
Чаще всего это называетсяshebang
fpmurphy

Ответы:

16

#!/bin/shдолжен работать на всех Unix и Unix-подобных дистрибутивах. Обычно он считается самым переносимым хэш-бангом, если ваш сценарий поддерживает POSIX. Предполагается, что /bin/shоболочка представляет собой оболочку, которая реализует стандарт оболочки POSIX, независимо от того, что представляет собой оболочка, маскирующаяся под /bin/shоболочку.


#!/bin/shобычно это просто ссылка, так как оболочка Bourne больше не поддерживается. Во многих системах Unix /bin/shэто будет ссылка /bin/kshили /bin/ash, во многих системах Linux на основе RHEL это будет ссылка /bin/bash, однако в Ubuntu и многих системах на основе Debian это ссылка /bin/dash. Все оболочки, при вызове как sh, перейдут в режим совместимости POSIX.

Хотя hashbang является важным заполнителем, поскольку он обеспечивает гораздо большую переносимость, чем другие методы, при условии, что ваш сценарий строго соответствует POSIX (повторяется, чтобы подчеркнуть важность).

Примечание. Когда bashвызывается в режиме POSIX, он по-прежнему допускает некоторые вещи [[, не относящиеся к POSIX, такие как массивы и многое другое. Эти вещи могут потерпеть неудачу в несистемной bash.

Jesse_b
источник
3
"в Ubuntu это ссылка на / bin / dash" и вообще на другие системы на основе Debian. IIRC на FreeBSD / GhostBSD также является символической ссылкой /bin/ash.
Сергей Колодяжный
Чтобы быть полностью переносимым, поместите пробел между shebang и (абсолютным) путем интерпретатора. Некоторые системы ищут #! /вместо просто #!.
Саймон Рихтер
5
@SimonRichter Ссылку на это было бы приятно видеть.
Кусалананда
@SergiyKolodyazhnyy Установленная версия sh во FreeBSD является пеплом, а не символической ссылкой.
Роб
2
@Kusalananda, хм, на этой странице написано, что это миф, так что, вероятно, его можно игнорировать.
Саймон Рихтер
12

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

Оболочка Борна была оболочкой, написанной в конце 70-х годов и заменившей предыдущую оболочку Томпсона (также называемую sh). В начале 80-х годов Дэвид Корн написал несколько расширений для оболочки Bourne, исправил несколько ошибок и смутил дизайн (и представил некоторые) и назвал ее оболочкой Korn.

В начале 90-х POSIX указывал sh язык на основе подмножества оболочки Korn, и большинство систем теперь изменили их /bin/shна либо оболочку Korn, либо оболочку, соответствующую этой спецификации. Что касается BSD, они постепенно меняли свою /bin/sh, первоначально (после того, как они больше не могли использовать оболочку Bourne по лицензионным причинам) оболочку Almquist, клон оболочки Bourne с некоторыми расширениями ksh, поэтому она стала POSIX-совместимой.

Сегодня все системы POSIX имеют оболочку sh(чаще всего, но не обязательно, в /binPOSIX не указывается путь к указанным утилитам), которая в основном совместима с POSIX. Обычно он основан на ksh88, ksh93, pdksh, bash, ash или zsh², но не на оболочке Bourne, поскольку оболочка Bourne никогда не была POSIX-совместимой3. Некоторые из этих оболочек ( bash, zsh, yashи некоторые pdkshпроизводные включить режим POSIX-совместимый при вызове , как shи в меньшей степени соответствует иным образом ).

bash(GNU-ответ на оболочку Korn) на самом деле является единственной оболочкой с открытым исходным кодом (и можно сказать, что поддерживается только в настоящее время, поскольку остальные, как правило, основаны на ksh88, который не получил никакой новой функции с 90-х годов), который был сертифицирован как быть POSIX-совместимым sh(как часть сертификации MacOS).

Когда вы пишете сценарий с #! /bin/sh -самоуверенностью, вы должны использовать стандартный shсинтаксис (а также использовать стандартный синтаксис для утилит, используемых в этом сценарии, если вы хотите быть переносимым, при интерпретации оболочки используется не только оболочка). script), тогда не имеет значения, какая реализация этого стандартного shсинтаксического интерпретатора используется ( ksh, bash...).

Неважно, что эти оболочки имеют расширения над стандартом, если вы их не используете. Это похоже на написание кода на C, если вы пишете стандартный код на C и не используете расширения одного компилятора (например gcc) или другого, ваш код должен компилироваться нормально, независимо от реализации компилятора, при условии, что компилятор совместим.

Здесь, с #! /bin/shона Взрыва, ваша главная задача будет системы , где /bin/shявляется Bourne оболочки , которая, например , не поддерживает стандартные функции , такие как $((1+1)), $(cmd), ${var#pattern}... В этом случае вам может понадобиться обходные как:

#! /bin/sh -
if false ^ true; then
  # in the Bourne shell, ^ is an alias for |, so false ^ true returns
  # true in the Bourne shell and the Bourne shell only.
  # Assume the system is Solaris 10 (older versions are no longer maintained)
  # You would need to adapt if you need to support some other system
  # where /bin/sh is the Bourne shell.
  # We also need to fix $PATH as the other utilities in /bin are
  # also ancient and not POSIX compatible.
  PATH=`/usr/xpg6/bin/getconf PATH`${PATH:+:}$PATH || exit
  export PATH
  exec /usr/xpg4/bin/sh "$0" "$@"
  # that should give us an environment that is mostly  compliant
  # to the Single UNIX Specification version 3 (POSIX.2004), the
  # latest supported by Solaris 10.
fi
# rest of the script

Кстати, Ubuntu /bin/shне bashпо умолчанию. В dashнаши дни это оболочка на основе NetBSD sh, сама основанная на оболочке Almquist, которая в основном совместима с POSIX, за исключением того, что она не поддерживает многобайтовые символы. В Ubuntu и других систем Debian на базе, вы можете выбрать между bashи dashдля /bin/shс dpkg-reconfigure dash) 4 . shСценарии, поставляемые с Debian, должны работать одинаково в обеих оболочках, так как они написаны в стандарте политики Debian (расширенный набор стандарта POSIX). Вы , вероятно, обнаружите , что они также работают КИ в zsh«S shэмуляции или bosh(вероятно , не , ksh93ни , yashкоторые не имеет localвстроенной команды (требуется политика Debian , но не POSIX)).

Все системы в unix.stackexchange.com имеют sh где-то POSIX . Большинство из них имеют /bin/sh(вы можете найти очень редкие, у которых нет /binкаталога, но вы, вероятно, не заботитесь об этом), и это, как правило, shинтерпретатор POSIX (а в редких случаях (нестандартная) оболочка Bourne вместо этого). ).

Но shэто единственный исполняемый файл интерпретатора оболочки, который вы наверняка найдете в системе. Для других оболочек вы можете быть уверены, что MacOS, Cygwin и большинство дистрибутивов GNU / Linux будут иметь bash. Производные от SYSV ОС (Solaris, AIX ...) обычно имеют ksh88, возможно, ksh93. OpenBSD, MirOS будет иметь производную от pdksh. MacOS будет иметь zsh. Но от этого не будет никакой гарантии. Никакой гарантии того, будет ли установлен bashили нет какой-либо из этих других корпусов в /binили где-либо еще (это обычно встречается в /usr/local/binBSD, например, при установке). И, конечно, нет гарантии, какая версия оболочки будет установлена.

Обратите внимание, что #! /path/to/executableэто не соглашение , это особенность всех Unix-подобных ядер ( представленных в начале 80-х годов Деннисом Ритчи ), которая позволяет выполнять произвольные файлы, указав путь к интерпретатору в первой строке, начиная с #!. Это может быть любой исполняемый файл.

При выполнении файла, первой строка начинается с #! /some/file some-optional-arg, ядро заканчивает выполнение /some/fileс some-optional-arg, путем сценария и исходными аргументами в качестве аргументов. Вы можете сделать эту первую строку, #! /bin/echo testчтобы увидеть, что происходит:

$ ./myscript foo
test ./myscript foo

Когда вы используете /bin/sh -вместо /bin/echo test, ядро ​​выполняет /bin/sh - ./myscript foo, shинтерпретирует код содержимого, хранящийся в, myscriptи игнорирует эту первую строку, поскольку это комментарий (начинается с #).


¹ Вероятно, единственная система сегодня, где кто-либо из нас сталкивался с системой /bin/sh, основанной на оболочке Bourne, - это Solaris 10. Solaris является одним из немногих Unix, которые решили сохранить там оболочку Bourne для обратной совместимости ( shязык POSIX не полностью обладает обратной совместимостью с оболочкой Bourne) и (по крайней мере, для настольных систем и полных серверов) имеет POSIX в shдругом месте (на /usr/xpg4/bin/shоснове ksh88), но это изменилось в Solaris 11, где /bin/shтеперь находится ksh93. Другие в основном несуществующие.

² из MacOS / X , используемый , чтобы быть , но позже изменено на . Это не основной фокус для использования в качестве реализации POSIX . Его режим в первую очередь для возможности вставлять или вызывать ( ) код POSIX в скриптах/bin/shzshbashzshshshsourceshzsh

³ Недавно @schily расширила оболочку OpenSolaris (основанную на оболочке SVR4, основанную на оболочке Bourne), чтобы она стала совместимой с POSIX, boshно я не знаю, используется ли она в любой системе. Наряду с ksh88этим делает его второй POSIX-совместимой оболочкой на основе кода оболочки Bourne.

4 В более старых версиях вы также можете использовать mkshили более lkshинкарнацию POSIX . Это оболочка MirOS (ранее MirBSD), основанная на pdksh, сама основанная на оболочке Forsyth (еще одно повторное воплощение оболочки Bourne))

Стефан Шазелас
источник
Незначительная вещь (о середине ответа шелл-кода): не следует ли использовать только exec sh "$0" "$@"после установки PATH? Это следует подобрать shиз правильного места, подумал бы я.
Кусалананда
1
@Kusalananda, это должно сработать, но это более рискованно, так как мы можем оказаться в бесконечном цикле, если что-то не так (например, sh с неправильными разрешениями, getconf Modified ...). В любом случае, остальное относится только к Solaris 10, поэтому мы также можем жестко закодировать все, включая содержимое $PATH.
Стефан
Любая цитата для OSX, использующая ksh в качестве оболочки POSIX. Его оболочкой по умолчанию была tcsh, но я не помню, чтобы bash не использовался, тем более что Korn Shell не был открытым исходным кодом до выхода OSX
user151019
1
@ Марк, я не говорил, что OSX когда-либо использовал ksh. Я сказал, что раньше это был zsh, и они переключились на bash (специальная сборка bash).
Стефан
1
@fpmurphy, если вы посмотрите на ранние версии, bash уже был на стороне ksh, где ksh не был совместим с оболочкой Bourne. В то время как вам пришлось ждать, пока bash2 достигнет того же уровня возможностей, что и ksh88, изначально в bash уже было несколько расширений ksh, таких как $(...)режимы emacs / vi, расширения тильды / скобки (те, которые изначально были из csh), fc, typeset, псевдоним , синтаксис функции в стиле ksh ...
Стефан
8

Да, вы можете использовать #!/bin/shв скрипте, потому что /bin/sh(мы надеемся) предусмотрены в таких системах, обычно по какой-то ссылке, которая bashведет себя (более или менее), как если shбы. Вот система Centos7, например, которая ссылается shна bash:

-bash-4.2$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Dec  4 16:48 /bin/sh -> bash
-bash-4.2$ 

Вы также можете использовать, #!/bin/bashесли вы пишете bashсценарий только для этой системы и хотите использовать bashфункции. Однако такие сценарии будут страдать от проблем с переносимостью, например, в OpenBSD, где bashон устанавливается, только если администратор берет на себя труд, чтобы установить его (а я нет), а затем он устанавливается /usr/local/bin/bash, а не /bin/bash. #!/bin/shСценарий строго POSIX должен быть более переносимым.

thrig
источник
Обратите внимание: «Когда shBash вызывается как , Bash переходит в режим POSIX после чтения файлов запуска». - gnu.org/software/bash/manual/bashref.html#Bash-POSIX-Mode
Гленн Джекман
2
Когда я пишу сценарии для серверов RHEL на своей работе, я использую их #!/bin/bashточно, чтобы использовать преимущества не-POSIX-функций.
Монти Хардер
@MontyHarder IIRC на RHEL /bin/shэто символическая ссылка bash, это правильно? Я точно знаю, что это CentOS.
Сергей Колодяжный
1
@SergiyKolodyazhnyy Да, на сервере RHEL, который я только что проверил, это символическая ссылка на bash. Двоичный файл знает, какое имя использовалось для его вызова, и когда он называется, shон действует как старая школа Борна sh.
Монти Хардер
6

Ты спрашивал

правильно ли размещать комментарий #! / bin / sh в сценариях оболочки, написанных в дистрибутивах ubuntu?

Ответ зависит от того, что вы пишете в сценарии оболочки.

  • Если вы строго используете переносимые POSIX-совместимые сценарии и не используете команды, специфичные для bash, тогда вы можете их использовать /bin/sh.

  • Если вы знаете, что когда-либо используете скрипт только на машине с bash, и вы хотите использовать специфичный для bash синтаксис, то вам следует использовать/bin/bash

  • Если вы хотите быть уверены, что сценарий будет работать на разных машинах Unix, вам следует использовать только POSIX-совместимый синтаксис, и/bin/sh

  • Если вы регулярно используете другую оболочку (например , КШ , ЗШ или Tcsh ), и вы хотите использовать этот синтаксис в скрипте, то вы должны использовать соответствующий интерпретатор (например /bin/ksh93, /bin/zshили /bin/tcsh)

Stobor
источник
3

"#!" комментарий не всегда использует /bin/bashили /bin/sh. Он просто перечисляет, каким должен быть интерпретатор, а не только для сценариев оболочки. Например, мои скрипты на Python обычно начинаются с #!/usr/bin/env python.

Теперь разница между #!/bin/shи в #!/bin/bashтом, что /bin/shне всегда является символической ссылкой на /bin/bash. Часто, но не всегда. Ubuntu является заметным исключением здесь. Я видел сценарии, работающие нормально в CentOS, но не работающие в Ubuntu, потому что автор использовал специфичный для bash синтаксис с #!/bin/sh.

aragaer
источник
1

Извините за то, что налил немного холодной воды на все эти замечательные ответы, в которых говорится, что /bin/shон присутствует во всех системах Unix - он присутствует, за исключением наиболее используемой системы Unix всех времен: Android.

В Android есть встроенная оболочка /system/bin/sh, и, как правило, нет возможности создать /bin/shссылку, даже в корневой системе (из-за того, что система заблокирована с помощью selinux и ограниченных наборов возможностей (7)).

Для тех, кто скажет, что android не является POSIX-совместимым: ни большинство дистрибутивов Linux и BSD. И существование /bin/shс этим путем не является обязательным стандартом :

Приложения должны учитывать, что PATHнельзя считать, что стандартом оболочки является /bin/shили /usr/bin/sh, и его следует определять путем запроса PATHвозвращаемого значения getconf PATH, обеспечивая, чтобы возвращаемый путь был абсолютным, а не встроенным в оболочку.

mosvy
источник
В то время как все системы, которые пытаются быть POSIX-совместимыми, имеют многочисленные ошибки соответствия (включая сертифицированные), Android (и большинство других встроенных систем, таких как ваш маршрутизатор или ОС с лампочкой) не предполагают совместимость с POSIX. Android здесь не по теме, кроме случаев, когда речь идет об интерфейсе POSIX.
Стефан
@ Кристофер, который getconf? Например, в Solaris 11.4, это будет в /usr/bin? Один в /usr/xpg4/bin(для соответствия SUSv2), другой в /usr/xpg6/bin(для соответствия SUSv3)? /usr/xpg7/bin(для SUSv4)?
Стефан
@ StéphaneChazelas, если мы решим судить о намерениях, я думаю, что могу легко найти некоторые цитаты Линуса Торвальдса или Тео де Раадта о том, что им наплевать на POSIX ;-) И большинство встроенных систем основаны на Linux + busybox, который чуть менее соответствует POSIX, чем типичная Unix-подобная система. А) андроид на самом деле не является «встроенной» системой и б) система андроида может быть сделана POSIX-совместимой без оболочки /bin/sh
mosvy
1
@mosvy, то, что вы говорите, в основном верно. Но Торвальдс может говорить только за ядро ​​Linux. Чет Рами заботится о соответствии POSIX для bash, RedHat заботится о соответствии POSIX (они спонсируют группу Austin и участвуют в их групповых собраниях, они поддерживают большую часть программного обеспечения GNU). Идея состоит в том, что POSIX является единственным стандартом, на который смотрит большинство Unix-подобных систем. Пользователи заботятся о POSIX-совместимости, поскольку это облегчает перенос программного обеспечения на разные системы. Это не идеально, но лучше, чем ничего. У Android есть собственный программный API, POSIX здесь не особо важен.
Стефан