Когда я должен использовать #! / Bin / bash, а когда #! / Bin / sh?

104

Когда это #!/bin/bashболее уместно, чем #!/bin/shв сценарии оболочки?

Hendré
источник
55
Когда вы используете bashфункции и синтаксис, а не shфункции и синтаксис.
Мокубай
3
Если это кому-нибудь поможет, я заметил, что это vimвыделит bash-измы, если у вашего сценария есть #!/bin/shшебанг. Я только изменяю это, bashесли вещи становятся достаточно волосатыми, чтобы начать нуждаться в bash-функциях.
SeldomNeedy
@SeldomNeedy Некоторые вещи, которые он выделяет по умолчанию, отлично работают в любой оболочке POSIX. $(...)особенно неприятно. Кроме того, некоторые из них неуловимы [ <(...)и cmd >& fileне выделяют никаких ошибок, например, у них просто нет специального выделения для того, что они имеют в виду, с или без g:is_bash]
Random832
@ Random832 Похоже, что в последнее время по этой теме было что-то в системе отслеживания проблем vim .
SeldomNeedy

Ответы:

150

Короче говоря:

  • Существует несколько оболочек, которые реализуют расширенный набор спецификации POSIX sh . В разных системах /bin/shможет быть ссылка на ash, bash, dash, ksh, zsh и т. Д. (Это всегда будет совместимо с sh - никогда не csh или fish.)

  • Пока вы придерживаетесь shтолько функций, вы можете (и, вероятно, даже должны) их использовать, #!/bin/shи скрипт должен работать нормально, независимо от того, какая это оболочка.

  • Если вы начнете использовать специфичные для bash функции (например, массивы), вам следует специально запросить bash - потому что, даже если /bin/shbash уже запущен в вашей системе, он может не работать в любой другой системе, и ваш скрипт не будет работать там. (Конечно, то же самое относится и к zsh и ksh.) Вы можете использовать shellcheck для определения bashisms.

  • Даже если скрипт предназначен только для личного использования, вы можете заметить, что некоторые операционные системы меняются /bin/shво время обновлений - например, в Debian он был bash, но позже был заменен на очень минимальный тире. Скрипты, которые использовали bashisms, но #!/bin/shвнезапно сломались.

Тем не мение:

  • Даже #!/bin/bashне очень правильно. В разных системах bash может жить в /usr/binили /usr/pkg/binили /usr/local/bin.

  • Более надежный вариант #!/usr/bin/env bash, который использует $ PATH. (Хотя сам envинструмент также не гарантирован, он /usr/bin/envвсе же работает на большем количестве систем, чем /bin/bashделает.)

grawity
источник
10
Хороший ответ. Вы хотели сделать это CW?
Мокубай
3
Просто замечание: если bash запущен, /bin/shон попытается имитировать shфункции (см. Справочную страницу ), но не уверен, что в этом режиме доступны определенные функции bash. envЭто инструмент posix, его нужно найти в большинстве дистрибутивов, но я не уверен, что некоторые могут не уважать posix.
Брайс
8
Нет, на самом деле POSIX определенно заявляет, что нельзя предполагать, что он находится в /bin: « Приложения должны учитывать, что стандартным PATH для оболочки нельзя считать либо / bin / sh, либо / usr / bin / sh, и его следует определить путем запроса PATH, возвращаемого getconf PATH, для обеспечения того, чтобы возвращаемый путь был абсолютным, а не встроенным в оболочку ". Также см. Этот вопрос и, в частности, этот ответ .
тердон
12
Хм, ну, значит, POSIX не определяет переносимый способ #!работы скриптов?
grawity
4
POSIX sh - это не Борн: это скорее производная от раннего ksh, чем прямой потомок Борна. Различить их легко: echo foo ^ catвыдает foo ^ catв POSIX sh, и выдает только fooв Bourne (так как там ^есть символ канала).
Чарльз Даффи
18

Используйте shebang, соответствующий оболочке, которую вы фактически использовали для разработки и отладки вашего скрипта. Т.е. если ваша оболочка входа в систему bashи вы запускаете свой скрипт как исполняемый в вашем терминале, используйте #!/bin/bash. Не просто предполагайте, что, поскольку вы не использовали массивы (или любую другую bashфункцию, о которой вам известно), вы можете выбрать любую оболочку, которая вам нравится. Существует много тонких различий между оболочками ( echoфункциями, циклами, как вы их называете), которые невозможно обнаружить без надлежащего тестирования.

Учтите это: если вы уйдете, #!/bin/bashа у ваших пользователей его нет, они увидят четкое сообщение об ошибке, что-то вроде

Error: /bin/bash not found

Большинство пользователей могут исправить это за одну минуту, установив соответствующий пакет. С другой стороны, если вы замените shebang на #!/bin/shи протестируете его в системе, где /bin/shесть символическая ссылка /bin/bash, у ваших пользователей, которые не имеют, bashбудут проблемы. Скорее всего, они увидят загадочные сообщения об ошибках, такие как:

Error in script.sh line 123: error parsing token xyz

Это может занять несколько часов, и не будет никакого понятия о том, какую оболочку они должны были использовать.

Существует не так много причин, почему вы захотите использовать другую оболочку в shebang. Одна из причин заключается в том, что используемая вами оболочка не получила широкого распространения. Другой способ - повысить производительность, shкоторая в некоторых системах значительно выше, и ваш сценарий станет узким местом в производительности. В этом случае тщательно протестируйте свой сценарий с помощью целевой оболочки, а затем измените шебанг.

Дмитрий Григорьев
источник
@Hastur Я не получил твой комментарий. Шебанг определенно определит, какая оболочка будет использоваться для запуска скрипта. Как вы думаете, из моего ответа следует обратное?
Дмитрий Григорьев
1
Забудь это. Я неправильно понял часть «почему вы хотели бы использовать» ...
Hastur
6

Вы должны только когда-либо использовать #! /bin/sh.

Вы никогда не должны использовать расширения bash (или zsh, или fish, или ...) в сценарии оболочки.

Вы должны когда-либо писать сценарии оболочки, которые работают с любой реализацией языка оболочки (включая все «служебные» программы, которые идут вместе с самой оболочкой). В эти дни вы, вероятно, можете считать POSIX.1-2001 ( не -2008) авторитетным для того, на что способны оболочка и утилиты, но помните, что однажды вас могут попросить перенести ваш скрипт в унаследованную систему (например, Solaris). или AIX), чьи оболочки и утилиты были заморожены около 1992 года.

Что, серьезно ?!

Да серьезно.

Вот в чем дело: Shell - ужасный язык программирования. Единственное, что у него есть /bin/sh- это единственный интерпретатор сценариев, который гарантированно будет в каждой установке Unix.

Вот еще одна вещь: некоторая итерация основного интерпретатора Perl 5 ( /usr/bin/perl) с большей вероятностью будет доступна в случайно выбранной установке Unix, чем (/(usr|opt)(/(local|sfw|pkg)?)?/bin/bashесть. Другие хорошие языки сценариев (Python, Ruby, node.js и т. Д. - я даже включу PHP и Tcl в эту категорию при сравнении с оболочкой) также примерно так же доступны, как bash и другие расширенные оболочки.

Поэтому, если у вас есть возможность написания bash-скрипта, у вас есть возможность использовать не просто ужасный язык программирования.

Теперь, простые сценарии оболочки, такие, которые запускают несколько программ последовательно из задания cron или чего-то еще, нет ничего плохого в том, чтобы оставить их как сценарии оболочки. Но простые сценарии оболочки не нуждаются в массивах, функциях или [[даже. И вы должны писать сложные сценарии оболочки только тогда, когда у вас нет другого выбора. Например, сценарии Autoconf по-прежнему являются сценариями оболочки. Но эти сценарии должны выполняться при каждом воплощении, /bin/shкоторое имеет отношение к настраиваемой программе. и это означает, что они не могут использовать какие-либо расширения. Возможно, вам не нужно заботиться о старых проприетарных Unix-файлах в наши дни, но вам, вероятно, следует заботиться о текущих BSD с открытым исходным кодом, некоторые из которых не устанавливаютсяbashпо умолчанию, и встроенные среды, которые дают вам только минимальную оболочку и busybox.

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

zwol
источник
4
Извините, но это немного глупо. Да, если вы пишете что-то, что будет i) распространяться и ii) в разных средах, вам следует придерживаться базовых принципов sh. Это, однако, далеко от заявления о том, что вы никогда не должны использовать другие оболочки. Каждый день пишутся тысячи (а может быть, и больше) сценариев, и подавляющее большинство из них будет работать только на одной машине. Ваш совет имеет смысл для производственного кода, но не для ежедневных «sysdaminy» заданий, для которых написано большинство сценариев. Если я знаю, что мой скрипт будет когда-либо использоваться только мной и на машине с Linux, bash подойдет.
тердон
1
@terdon Суть в том, что если у вас есть возможность написать сценарий bash, то у вас также есть возможность написать сценарий perl (или любой другой), и это всегда лучший выбор.
Звол
@terdon Ответ, конечно, не глупый, ваш комментарий вместо этого просто наивный. Скрипты оболочки ужасны для отладки по сравнению с Perl и тому подобным, для которого можно легко использовать полные интегрированные среды разработки с графической отладкой и все такое. Кроме того, большинство сценариев, написанных каждый день для какой-то мелочи, имеют тенденцию изменяться и изменяться снова, расти, копироваться в качестве примеров для коллег или в сети и т. Д. Скорее всего, нет причин идти на такой риск.
Торстен Шенинг
@ ThorstenSchöning Я сказал немного глупо. Если бы это были Unix & Linux или даже Stack Overflow, я бы даже согласился. Если мы говорим об общем передовом опыте или профессиональных программистах, конечно, лучше придерживаться базового POSIX sh. Тем не менее, это Super User , сайт, предназначенный для конечных пользователей и говорящий людям никогда не использовать bash или zsh при написании сценариев оболочки, на мой взгляд, крайне.
Тердон
@terdon На самом деле , на мой взгляд, более важно не давать конечным пользователям писать сложные сценарии оболочки. Профессионалы имеют в среднем более высокий уровень квалификации (таким образом, они с большей вероятностью смогут справиться с ошибками сложных сценариев оболочки), должны с некоторой точностью знать, к каким средам они должны быть переносимы, и им платят, чтобы они не отказывались в отчаянии.
zwol 16.10.16
4

Как правило, если время важнее, чем функциональность, вы будете использовать более быструю оболочку. sh часто имеет псевдоним dash и, как правило, используется для задач cron root или пакетных операций, где каждая (нано) секунда считается.

mckenzm
источник
2
Загрузка дополнительного интерпретатора оболочки из файловой системы может свести на нет такое преимущество, и все, что подсчитывает наносекунды (которые имеют тот же порядок величины, что и фактический машинный цикл), является областью программирования C и ассемблера.
стеганые кости
Наносекунды имеют значение только в заданиях cron, если у вас их есть миллиарды, и cron все равно не сможет справиться со многими из них.
Дмитрий Григорьев
1
Даже если mckenzm немного преувеличил, нельзя отрицать, что больше производительности лучше! Не зацикливайтесь на "нано" части. (Наносекунды даже не считаются в C, если это не внутренний цикл в системе реального времени или чем-то подобном!)
jpaugh
1
@jpaugh Если вы не работаете с (устаревшей) системой, которая предполагает, что определенные операции будут занимать как минимум определенный период времени. Такое случается!
JAB
3

Чтобы быть еще более кратким, используйте, shесли переносимость между большинством систем наиболее важна, и bashесли вы хотите использовать некоторые из его специфических функций, таких как массивы, если версия bash поддерживает это.

Спенсер Уильямс
источник
1
  • sh (в большинстве случаев), bash, если это требуется (или ksh, или что-то еще)

Самый безопасный путь. когда он жестко запрограммирован, будет / usr / bin /, shellOfChoiceно сейчас я всегда пытаюсь использовать новое соглашение, поскольку «местоположение по умолчанию» при изменении PATH может измениться так:

#! / usr / bin / env sh или
#! / usr / bin / env bash
или, например, для сценариев perl
#! / usr / bin / env perl -w

Конечно, когда у вас есть причина, по которой скрипт НИКОГДА не будет автоматически принимать новый PATH, продолжайте жестко его кодировать, и тогда / usr / bin / что-то должно быть наиболее вероятным путем для использования.

Майкл Фетт
источник