Слишком много строк shebang (объявление скрипта) - есть ли способ уменьшить их количество?


12

У меня есть проект, состоящий из около 20 небольших .shфайлов. Я называю их «маленькими», потому что обычно ни один файл не содержит более 20 строк кода. Я выбрал модульный подход, потому что, таким образом, я верен философии Unix, и мне легче поддерживать проект.

В начале каждого .shфайла я ставлю #!/bin/bash.

Проще говоря, я понимаю, что объявления скриптов имеют две цели:

  1. Они помогают пользователю вспомнить, какая оболочка необходима для выполнения файла (скажем, через несколько лет без использования файла).
  2. Они гарантируют, что скрипт выполняется только с определенной оболочкой (в данном случае Bash), чтобы предотвратить непредвиденное поведение в случае использования другой оболочки.

Когда проект начинает расти, скажем, с 5 файлов до 20 файлов или с 20 файлов до 50 файлов (не в этом случае, а просто для демонстрации), у нас есть 20 или 50 строк объявлений сценариев. Я признаю, что хотя это может показаться забавным для некоторых, мне кажется немного излишним использовать 20 или 50 вместо того, чтобы говорить только 1 на проект (возможно, в основном файле проекта).

Есть ли способ избежать этой предполагаемой избыточности в 20 или 50 или намного большего числа строк объявлений скриптов, используя некое «глобальное» объявление скриптов в каком-то основном файле?


2
Вы не должны, но могли бы; удалите его и выполните все свои сценарии с помощью/bin/bash -x "$script"
jesse_b


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

Ответы:


19

Даже если ваш проект теперь может состоять исключительно из 50 скриптов Bash, он рано или поздно начнет накапливать скрипты, написанные на других языках, таких как Perl или Python (из-за преимуществ, которые эти скриптовые языки имеют, которых нет у Bash).

Без правильной #!строки в каждом сценарии было бы чрезвычайно трудно использовать различные сценарии, не зная, какой интерпретатор использовать. Не имеет значения, выполняется ли каждый отдельный скрипт из других скриптов , это только переносит сложность от конечных пользователей к разработчикам. Ни одна из этих двух групп людей не должна знать, на каком языке написан сценарий, чтобы иметь возможность его использовать.

Сценарии оболочки, выполняемые без #!строки и без явного интерпретатора, выполняются по-разному, в зависимости от того, какая оболочка их вызывает (см., Например, вопрос « Какой интерпретатор оболочки запускает сценарий без шебанга?» И особенно ответ Стефана ), а это не то, что вам нужно в производственной среде (вам нужно согласованное поведение и, возможно, даже переносимость).

Скрипты, выполняемые с явным интерпретатором, будут запускаться этим интерпретатором независимо от того, что #!говорит -line. Это вызовет проблемы в дальнейшем, если вы решите повторно реализовать, скажем, скрипт Bash на Python или любом другом языке.

Вы должны потратить эти дополнительные нажатия клавиш и всегда добавлять #!-линии к каждому сценарию.


В некоторых средах в каждом сценарии в каждом сценарии содержатся стандартные текстовые юридические тексты. Будьте очень рады, что это всего лишь #!линия, которая кажется «избыточной» в вашем проекте.


Ответ должен быть расширен следующим образом: оболочка определяет интерпретатор по #!строке, как только файл имеет доступное разрешение и загружается не интерпретатором, а оболочкой. То есть /path/to/myprog.plзапускает perl-программу, если #!есть строка (указывающая на интерпретатор perl) и файл имеет разрешение exec.
rexkogitans

11

Вы неправильно поняли смысл #!. На самом деле это указание операционной системе запускать эту программу под определенным интерпретатором.

Таким образом, сценарий может быть #!/usr/bin/perlи результирующая программа может быть запущена как ./myprogramи Perl интерпретатор может быть использован. Подобное #!/usr/bin/pythonприведет к запуску программы под python.

Таким образом, строка #!/bin/bashуказывает ОС запускать эту программу под bash.

Вот пример:

$ echo $0
/bin/ksh

$ cat x
#!/bin/bash

ps -aux | grep $$

$ ./x
sweh      2148  0.0  0.0   9516  1112 pts/5    S+   07:58   0:00 /bin/bash ./x
sweh      2150  0.0  0.0   9048   668 pts/5    S+   07:58   0:00 grep 2148

Таким образом, несмотря на то, что моя оболочка - ksh, программа "x" запускается в bash из-за #!строки, и мы можем увидеть это явно в списке процессов.


ps -auxможет дать предупреждение,
Вейцзюнь Чжоу

@ WeejunZhou Скажи больше ...
Кусалананда

Некоторая версия psдает предупреждение Warning: bad syntax, perhaps a bogus '-'?.
Вейцзюнь Чжоу

2
pssuooprts и синтаксис аргументов BSD и UXIX . -auxнеправильно UNIX Стивен, вероятно, означает, auxчто это правильно BSD
Jasen

1
Люди, 2 вещи; (1) сосредоточиться на примере концепции (что psпоказывает вызов bash), а не на явном синтаксисе; (2) ps -auxотлично работает на CentOS 7 и Debian 9 без предупреждений; эта паста была именно результатом моей машины; все обсуждение того -, нужен ли он или нет, применимо к более ранним версиям Linux-процедур, а не к текущим версиям.
Стивен Харрис

9

На .sh

Что плохо, это .shв конце имени файла.

Представьте, что вы переписываете один из скриптов на python.

Что ж, теперь вам нужно изменить первую строку, #!/usr/bin/python3это не так уж и плохо, так как вам нужно было также изменить каждую строку кода в файле. Однако вы также должны изменить имя файла с prog.shна prog.py. И затем вам нужно найти повсюду в других скриптах и ​​всех ваших пользовательских скриптах (те, о которых вы даже не подозревали), и изменить их, чтобы использовать новый скрипт. sed -e 's/.sh/.py/g'может помочь тем, о ком ты знаешь. Но теперь ваш инструмент контроля версий показывает изменение во множестве несвязанных файлов.

В качестве альтернативы делать это так , Unix и название программы progне prog.sh.

На #!

Если у вас есть правильный #!, и установите разрешение / режим, чтобы включить выполнение. Тогда вам не нужно знать переводчика. Компьютер сделает это за вас.

chmod +x prog #on gnu adds execute permission to prog.
./prog #runs the program.

Для не-GNU систем прочитайте руководство для chmod(и изучите восьмеричное), возможно, прочитайте его в любом случае.


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

@SergiyKolodyazhnyy Но вам не нужно знать язык, вам просто нужно его запустить. Даже Microsoft пытается отойти от расширений (к сожалению, это я их скрываю). Однако я согласен, что они могут быть хорошей идеей: .imageдля фотографий. .audioдля звука и т. д. Таким образом, говоря вам, что это такое, но не о реализации / кодировании.
Ctrl-Alt-Delor

1
@ ctrl-alt-delor Если файл вызывается launch-missilesили delete-project-and-make-manager-pissedя не хочу "просто запустить его", когда мне было интересно узнать о языке :)
Сергей Колодяжный,

2
если вам интересно, используйте fileкоманду. он распознает большинство типов файлов.
Jasen

2
Я согласен, что расширения являются плохими для исполняемых скриптов, именно по указанным причинам. Я использую расширение .sh для скрипта, который должен быть получен другим скриптом оболочки (т.е. неисполняемым скриптом) - в этом сценарии суффикс важный / действительный, потому что он говорит нам, как мы можем использовать файл.
Лоуренс Реншоу

8

[Строки hashbang] обеспечивают выполнение сценария только с определенной оболочкой (в данном случае Bash), чтобы предотвратить непредвиденное поведение в случае использования другой оболочки.

Вероятно, стоит отметить, что это совершенно неправильно. Строка hashbang никоим образом не мешает вам пытаться передать файл неправильной оболочке / интерпретатору:

$ cat array.sh
#!/bin/bash
a=(a b c)
echo ${a[1]} $BASH_VERSION
$ dash array.sh
array.sh: 2: array.sh: Syntax error: "(" unexpected

(или аналогично с awk -f array.shт. д.)


Но в любом случае, другой подход к модульности должен был бы определить функции оболочки в файлах, одну функцию на файл, если хотите, и затем sourceфайлы из основной программы. Таким образом, вам не понадобятся хэш-банги: они будут рассматриваться как любые другие комментарии, и все будет работать с использованием одной и той же оболочки. Недостатком будет то, что вам понадобятся sourceфайлы с функциями (например for x in functions/*.src ; do source "$x" ; done), и все они будут работать в одной оболочке, поэтому вы не сможете напрямую заменить одну из них на Perl-реализацию.


+1 например с массивами bash. Пользователи, незнакомые с несколькими оболочками или с некоторыми определениями POSIX, могут предположить, что некоторые функции одной оболочки существуют в другой. Также для функций это может быть актуально: unix.stackexchange.com/q/313256/85039
Сергей Колодяжный,

4

Есть ли способ избежать этой предполагаемой избыточности в 20 или 50 или намного большего числа строк объявлений скриптов, используя некое «глобальное» объявление скриптов в каком-то основном файле?

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

Однако переносимые скрипты не спасут вас от:

  • необходимость использовать особенности одной из оболочек ( пример ответа Илккачу )
  • необходимость использовать языки сценариев более продвинутые, чем оболочки (Python и Perl)
  • Ошибка PEBKAC: ваш скрипт попадает в руки пользователя J. Doe, который запускает ваш скрипт не так, как вы предполагали, например, они запускают /bin/shскрипт с csh.

Если я могу высказать свое мнение, «избегать избыточности» путем устранения #!является неправильной целью. Целью должна быть стабильная производительность и фактически работающий скрипт, который работает и рассматривает ключевые случаи, следуя определенным общим правилам, таким как not-parsing-ls или всегда-цитирование-переменные-если-не-необходимость-разбиение слов .

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

Они действительно не для пользователя - они для операционной системы, чтобы запустить надлежащий интерпретатор. «Правильный» в данном случае означает, что ОС находит то, что находится в шебанге; если код ниже его для неправильной оболочки - это вина автора. Что касается пользовательской памяти, я не думаю, что «пользователю» таких программ, как Microsoft Word или Google Chrome, когда-либо нужно было знать, на каких языках написаны программы; авторы могут понадобиться.

С другой стороны, сценарии, написанные переносимо, могут избавить от необходимости даже помнить, какую оболочку вы использовали изначально, потому что переносимые сценарии должны работать в любой POSIX-совместимой оболочке.

Они гарантируют, что скрипт выполняется только с определенной оболочкой (в данном случае Bash), чтобы предотвратить непредвиденное поведение в случае использования другой оболочки.

Они не Что на самом деле предотвращает неожиданное поведение, так это написание переносимых скриптов.


1

Причина, по которой вам нужно поместить #!/bin/bashв начало каждого скрипта, заключается в том, что вы запускаете его как отдельный процесс.

Когда вы запускаете скрипт как новый процесс, ОС видит, что это текстовый файл (в отличие от двоичного исполняемого файла), и ей нужно знать, какой интерпретатор выполнять. Если вы этого не скажете, ОС может только догадываться, используя настройки по умолчанию (которые может изменить администратор). Таким образом, #!строка (плюс, конечно, добавление разрешений на выполнение) превращает простой текстовый файл в исполняемый файл, который можно запускать напрямую, с уверенностью, что ОС знает, что с ним делать.

Если вы хотите удалить #!строку, то ваши варианты:

  1. Выполните сценарий напрямую (как вы делаете сейчас) и надейтесь, что интерпретатор по умолчанию соответствует сценарию - вы, вероятно, в безопасности на большинстве систем Linux, но не переносимы на другие системы (например, Solaris), и его можно изменить на что угодно (я мог даже настроить его vimна файл!).

  2. Выполните скрипт, используя bash myscript.sh. Это прекрасно работает, но вы должны указать путь к сценарию, и это грязно.

  3. Использовать исходный код сценария . myscript.sh, который запускает сценарий в текущем процессе интерпретатора (т.е. не как подпроцесс). Но это почти наверняка потребует изменений в ваших скриптах и ​​сделает их менее модульными / многократно используемыми.

В случаях 2 и 3, файл не нуждается (и не должен иметь) разрешений на выполнение, и предоставление ему .sh(или .bash) суффикса - хорошая идея.

Но ни один из этих вариантов не выглядит подходящим для вашего проекта, так как вы сказали, что хотите, чтобы ваши скрипты были модульными (=> независимыми и автономными), поэтому вы должны держать свою #!/bin/bashстроку в верхней части скриптов.

В своем вопросе вы также сказали, что придерживаетесь философии Unix. Если это правда, тогда вы должны узнать, для чего эта #!строка на самом деле, и продолжать использовать ее в каждом исполняемом скрипте!


Спасибо за хороший ответ. Я любил все в ответе и думал о голосовании до этого then you should learn what the #! line is really for, and keep using it in every executable script!. Можно следовать философии У. до определенной точки знания. После всех этих ответов я понял, почему его можно включить в этот термин. Я предлагаю отредактировать ответ, заменив его на «Посмотрите на Шебанг как на внутреннюю часть философии Unix, которую вы уважаете и принимаете».
user9303970

1
Извините, если я был прямым, или если этот комментарий показался грубым. Ваши комментарии предполагают, что вы предпочитаете работать в IDE - с «проектом», который позволяет вам определять глобальные правила для сценариев в этом проекте. Это верный способ разработки, но он не подходит для того, чтобы рассматривать каждый скрипт как независимую автономную программу (философия Unix, на которую вы ссылались).
Лоуренс Реншоу
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.