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

12

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

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

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

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

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

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

user9303970
источник
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(и изучите восьмеричное), возможно, прочитайте его в любом случае.

Ctrl-Alt-Делор
источник
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-реализацию.

ilkkachu
источник
+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, на которую вы ссылались).
Лоуренс Реншоу