Чи визначає shebang оболонку, яка запускає сценарій?


84

Це може бути дурним питанням, але я його все ж задаю. Якщо я оголосив шебанг

#!/bin/bash 

на початку my_shell_script.sh, тому я завжди повинен викликати цей скрипт за допомогою bash

[my@comp]$bash my_shell_script.sh

чи можу я використовувати, наприклад

[my@comp]$sh my_shell_script.sh

і мій сценарій визначає запущену оболонку за допомогою shebang? Це те ж саме відбувається з kshоболонкою? Я використовую AIX.


6
з вашого боку є невелика плутанина: коли ви робите "_some_shell some_script", він запускає _some_shell і просить його інтерпретувати some_script. Так що ні, якщо ви робите "sh my_shell_script.sh", він не інтерпретуватиме shebang, але інтерпретує сценарій замість sh. Щоб скористатися шебангом: chmod +x my_shell_script.sh ; /path/to/my_shell_script.sh # or ./my_shell_script.sh if you happen to be in its directory
Олів'є Дулак

Відповіді:


117

Кубло #! є читаним людиною екземпляр магічного числа , що складається з байтів рядка 0x23 0x21, яка використовується в exec()сімействі функцій для визначення , чи є файл , який буде виконуватися сценарій або двійковим. Коли шебанг присутній, exec()замість нього запуститься виконаний файл, вказаний після шебангу.

Зауважте, що це означає, що якщо ви викликаєте скрипт, вказавши інтерпретатора в командному рядку, як це робиться в обох випадках, наведених у питанні, exec()виконає інтерпретатор, вказаний у командному рядку, він навіть не буде розглядати сценарій.

Отже, як зазначали інші, якщо ви хочете exec()викликати інтерпретатора, вказаного в рядку shebang, сценарій повинен мати встановлений виконуваний біт і викликати як ./my_shell_script.sh.

Поведінку легко продемонструвати за допомогою наступного сценарію:

#!/bin/ksh
readlink /proc/$$/exe

Пояснення:

  • #!/bin/kshвизначає kshяк перекладача.

  • $$ містить PID поточного процесу.

  • /proc/pid/exe є символьним посиланням на виконуваний файл (принаймні, на Linux; в AIX /proc/$$/object/a.out є посиланням на виконуваний файл).

  • readlink виведе значення символічного посилання.

Приклад:

Примітка : я показую це на Ubuntu, де оболонка за замовчуванням /bin/shє символічною посиланням на тир ІЕ /bin/dashі /bin/kshє символічною посиланням /etc/alternatives/ksh, яка , в свою чергу , є символічною посиланням на /bin/pdksh.

$ chmod +x getshell.sh
$ ./getshell.sh 
/bin/pdksh
$ bash getshell.sh 
/bin/bash
$ sh getshell.sh 
/bin/dash

дякую Томасу за цю відповідь. Прикиньмо, що ми запускаємо скрипт як дочірній процес з Node.js або Java чи будь-якого іншого. Чи можемо ми запустити "exec" процес, і тоді exec запустить скрипт оболонки? Я прошу босе, шукаю відповіді на це питання: stackoverflow.com/questions/41067872/…
Олександр Міллс

1
@AlexanderMills Посиланням exec()у цій відповіді є системний виклик, команда exec- це вбудована оболонка, тому ви не можете викликати exec програму з Node.js або Java. Однак будь-яка команда оболонки, яка викликається, наприклад, Runtime.exec()на Java, врешті-решт обробляється exec()системним викликом.
Томас Найман

Так, так, я знайомий з Java API, який ви вже згадали, мені цікаво, чи є спосіб викликати виклик exec () нижнього рівня з Node.js якось
Олександр Міллз

@AlexanderMills, я думаю, child_process.{exec(),execFile(),spawn()} все буде реалізовано за допомогою C exec()(через process).
Томас Найман

10

Так. До речі, це не дурне питання. Посилання на мою відповідь тут . Запуск сценарію з #!

  • Його називають шебангом або лінією "чуб".

  • Це не що інше, як абсолютний шлях до інтерпретатора Баша.

  • Він складається із знака числа та символу оклику (#!), Після якого йде повний шлях до перекладача, такого як / bin / bash.

    Усі сценарії під Linux виконуються за допомогою інтерпретатора, визначеного в першому рядку Майже всі скрипти bash часто починаються з #! / Bin / bash (якщо припустити, що Bash встановлено в / bin) Це гарантує, що Bash буде використовуватися для інтерпретації сценарію, навіть якщо він виконаний під іншою оболонкою. Шебанг був введений Деннісом Річі між версією 7 Unix та 8 у Bell Laboratories. Потім його також додали до лінії BSD в Берклі.

Ігнорування лінії перекладача (shebang)

Якщо ви не вказуєте рядок інтерпретатора, типовим є / bin / sh. Але рекомендується встановити рядок #! / Bin / bash.


3
Для розробки ядро ​​знає лише, як виконати статично пов'язані двійкові файли та де знайти інформацію про інтерпретатора для інших (спеціальне поле у ​​двійковій чи рядковій лінії). Зазвичай виконання сценарію оболонки означає слідування лінії shebang до оболонки, а потім слідування за полем DT_INTERP у двійковій оболонці до динамічного лінкера.
Саймон Ріхтер

5
Також зауважте, що це не обмежується скриптами оболонки. Усі текстові файли скриптів використовують це. Наприклад, ще #!/usr/bin/perl #!/usr/local/bin/python #!/usr/local/bin/rubyодна поширена запис shebang, що використовується для підтримки декількох систем, - це використовувати env для пошуку перекладача, який ви хочете використовувати, наприклад#!/usr/bin/env perl #!/usr/bin/env python
sambler

@sambler кажучи про те env, чого слід насправді віддати перевагу? Python та Perl часто використовують env, тоді як на оболонках, це часто опускається, і shebang вказує на відповідну оболонку.
полемон

1
@polemon, що менше бажано, і більше, на яких шляхах змінюються. Основні оболонки йдуть однаковим чином у всіх системах. Оновлені версії perl та python можуть бути встановлені в різних місцях у різних системах, тому використання env дозволяє тому самому shebang завжди працювати, саме тому env використовується більше для скриптів perl та python, ніж сценарії оболонки.
самблер

envзнайти програму в $ PATH - це трохи хак. Він не встановлює змінні середовища, як випливає з назви. $ PATH може бути різним результатом для різних користувачів. Але це допомагає сценаріям працювати без змін у системах, які розміщують розумний інтерпретатор perl в якомусь дивному місці.
Джон Маховавальд

4

execСистемний виклик ядра Linux розуміє shebangs ( #!) спочатку

Коли ви робите баш:

./something

в Linux це викликає execсистемний виклик шляхом ./something.

Цей рядок ядра викликає файл, переданий на адресу exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

Він читає найперші байти файлу і порівнює їх #!.

Якщо порівняння вірно, то решта рядка аналізується ядром Linux, що робить ще один execвиклик із шляхом /usr/bin/env pythonта поточним файлом як перший аргумент:

/usr/bin/env python /path/to/script.py

і це працює для будь-якої мови сценаріїв, яка використовується #як символ коментаря.

І так, ви можете зробити нескінченну петлю за допомогою:

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

Bash розпізнає помилку:

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#! просто буває читатими людиною, але цього не потрібно.

Якщо файл починався з різних байтів, то execсистемний виклик використовував би інший обробник. Інший найважливіший вбудований обробник призначений для виконуваних файлів ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305, який перевіряє наявність байтів 7f 45 4c 46(що також буває людиною) читабельна для .ELF). Давайте підтвердимо, що, прочитавши 4 перших байта /bin/ls, який є виконуваним ELF:

head -c 4 "$(which ls)" | hd 

вихід:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

Отже, коли ядро ​​бачить ці байти, воно бере файл ELF, правильно вводить його в пам'ять і запускає новий процес з ним. Дивіться також: https://stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861

Нарешті, ви можете додати власні обробники шебанг із binfmt_miscмеханізмом. Наприклад, ви можете додати спеціальний обробник .jarфайлів . Цей механізм навіть підтримує обробники за допомогою розширення файлу. Інша програма полягає у прозорому запуску виконуваних файлів іншої архітектури за допомогою QEMU .

Однак я не думаю, що POSIX не вказує shebangs: https://unix.stackexchange.com/a/346214/32558 , хоча це згадується в розділах обґрунтування та у формі "якщо виконувані сценарії підтримуються системою щось може статися ».


1
Запуск ./somethingз оболонки не пройде повний шлях до exec, але саме введений шлях. Чи можете ви виправити це у своїй відповіді? Зробіть echo "$0"у своєму сценарії, і ви побачите, що це так.
AndiDog

2

Насправді, якщо взяти це відповідно, виконуваний файл, зазначений у рядку shebang, - це лише виконуваний файл. Є сенс використовувати якийсь інтерпретатор тексту як виконуваний, але це не обов'язково. Тільки для роз’яснення та демонстрації я зробив досить марний тест:

#!/bin/cat
useless text
more useless text
still more useless text

Названий файл test.txt і встановити exectuable біт chmod u+x test.txt, то «під назвою» це: ./test.txt. Як і очікувалося, вміст файлу виводиться. У цьому випадку кішка не ігнорує лінію шебанга. Він просто виводить всі рядки. Будь-який корисний перекладач повинен мати можливість ігнорувати цю лінію шебанг. Для bash, perl та PHP це просто рядок коментарів. Так що так, ці ігнорують лінію shebang.


-1

З того, що я зібрав, щоразу, коли для файлу встановлено виконуваний біт і його викликається, ядро ​​аналізує заголовок файлу, щоб визначити, як діяти (наскільки я знаю, ви можете додати користувацькі обробники для власних форматів файлів через LKM). Якщо цей файл є текстовим файлом з символом #! комбінація на початку, її виконання відправляється до іншого виконуваного файлу (як правило, оболонки сортів), шлях до якого повинен бути вказаний безпосередньо після зазначеного шебангу, в тому ж рядку. Потім ядро ​​приступає до виконання оболонки і передає файл для оброблення.

Коротше кажучи, не має значення, з якою оболонкою ви будете викликати скрипт - ядро ​​в будь-якому випадку відправить виконання у відповідний варіант.


4
Існує помітна різниця між bash ./myscript.shта ./myscript.sh.
CVn

Що ви маєте на увазі під цією «помітною різницею»?
jrara

3
@jrara Дивіться мою відповідь: твердження, що "не має значення, з якою оболонкою ви викликаєте сценарій", просто не відповідає дійсності.
Thomas Nyman
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.