Занадто багато рядків 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 та без явного інтерпретатора, виконуються різними способами залежно від того, яка оболонка викликає їх (див., Наприклад, питання, який інтерпретатор оболонки запускає сценарій без шебангу? І особливо відповідь Стефана ), що не є тим, що ви хочете у виробничому середовищі (ви хочете послідовної поведінки та, можливо, навіть портативності).

Сценарії, виконані з явним перекладачем, виконуватимуться цим перекладачем незалежно від того, що #!говорить -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можуть попередити,
Вейджун Чжоу

@WeijunZhou Скажи більше ...
Kusalananda

Деяка версія програми psдає попередження Warning: bad syntax, perhaps a bogus '-'?.
Вейджун Чжоу

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

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

9

На .sh

Що погано - це .shв кінці імені файлу.

Уявіть, що ви переписали один із сценаріїв у python.

Ну а тепер вам доведеться змінити перший рядок на #!/usr/bin/python3це не так вже й погано, оскільки вам довелося також змінити і кожен інший рядок коду у файлі. Однак ви також повинні змінити ім'я файлу з prog.shна prog.py. Тоді вам доведеться скрізь знайти в інших сценаріях і всі ваші користувацькі сценарії (ті, про які ви навіть не знали), і змінити їх, щоб використовувати новий сценарій. sed -e 's/.sh/.py/g'може допомогти тим, про кого ви знаєте. Але тепер ваш інструмент контролю за версією показує зміну безлічі непов’язаних файлів.

Крім того, зробіть це способом Unix, а програму progне називайте prog.sh.

На #!

Якщо у вас є правильність #!, і встановіть дозвіл / режим для включення Execute. Тоді вам не потрібно знати перекладача. Комп’ютер зробить це за вас.

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командою. він розпізнає більшість типів файлів.
Ясен

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

8

[Рядки хешбангу] гарантують, що сценарій працює лише з певною оболонкою (в цьому випадку Bash), щоб запобігти несподіваній поведінці у випадку, якщо використовується інша оболонка.

Напевно, варто зазначити, що це зовсім неправильно. Рядок хешбангу жодним чином не заважає вам намагатися подати файл на неправильну оболонку / інтерпретатор:

$ 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).

Однак портативні сценарії не рятують вас від:

  • потрібно використовувати функції однієї з оболонок (відповідь ilkkachu - приклад)
  • потрібно використовувати мови сценаріїв більш досконалі, ніж оболонки (Python та Perl)
  • Помилка PEBKAC: ваш сценарій потрапляє до рук користувача J.Doe, який запускає ваш сценарій не так, як ви цього задумали, наприклад, за допомогою якого вони виконують /bin/shскрипт csh.

Якщо я можу висловити свою думку, "уникнути надмірності" шляхом усунення #!- це неправильна мета. Мета повинна бути послідовною продуктивністю та власне робочим сценарієм, який працює і розглядає кутові випадки, дотримуючись певних загальних правил, таких як не-розбір 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. Якщо це правда, то вам слід дізнатися, для чого #!насправді потрібна лінія, і продовжувати використовувати її у кожному виконуваному сценарії!


Дякую за хорошу відповідь. Я любив все , що у відповідь і думав не upvoting до цього: 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.