Який інтерпретатор оболонки виконує сценарій без шебанга?


17

Припустимо, що для мого облікового запису за замовчуванням є zsh, але я відкрив термінал і запустив bash та виконав сценарій з назвою prac002.sh, який інтерпретатор оболонки буде використовуватися для виконання сценарію, zsh чи bash? Розглянемо наступний приклад:

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % sudo cat /etc/passwd | grep papagolf
[sudo] password for papagolf: 
papagolf:x:1000:1001:Rex,,,:/home/papagolf:/usr/bin/zsh
# papagolf's default shell is zsh

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % bash
# I fired up bash. (See that '%' prompt in zsh changes to '$' prompt, indicating bash.)

papagolf@Sierra:~/My Files/My Programs/Learning/Shell$ ./prac002.sh 
Enter username : Rex
Rex
# Which interpreter did it just use?

** EDIT: ** Ось зміст сценарію

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh 
read -p "Enter username : " uname
echo $uname

Що пише сценарій вгорі?
Майкл Гомер

@MichaelHomer: Нічого. Я щойно відредагував питання, щоб додати вміст сценарію. Сценарій має дозвіл на виконання.
7_R3X

Якщо ви хочете використовувати поточну оболонку, точково . prac002.shвкажіть її, як у , припускаючи, що ваш сценарій знаходиться у поточному режимі.
thecarpy

1
Запустіть, . ./prac002.shі він запуститься з поточною оболонкою, тобто крапкою ( .), пробілом (), за яким слід шлях вашого сценарію. Це називається точковим джерелом вашого сценарію. ;-)
thecarpy

Відповіді:


19

Оскільки сценарій не починається з #!рядка shebang, який вказує, який інтерпретатор використовувати, POSIX говорить :

Якщо execl()функція не спрацьовує через помилку, еквівалентну помилці [ENOEXEC], визначеній в томі системних інтерфейсів POSIX.1-2008, оболонка повинна виконати команду, еквівалентну тому, що оболонка викликається іменем шляху, отриманим в результаті пошуку, як її першим операнд , причому будь-які інші аргументи передаються до нової оболонки, за винятком того, що значення "$ 0" у новій оболонці може бути встановлено на ім'я команди. Якщо виконуваний файл не є текстовим файлом, оболонка може обійти це виконання команди. У цьому випадку він пише повідомлення про помилку та повертає статус виходу 126.

Таке фразування є дещо неоднозначним, і різні оболонки мають різні трактування.

У цьому випадку Bash запустить сценарій, використовуючи сам . З іншого боку, якщо ви запустили його з zsh, замість цього, zsh використовував биsh (що б там не було у вашій системі).

Ви можете перевірити цю поведінку для цього випадку, додавши ці рядки до сценарію:

echo $BASH_VERSION
echo $ZSH_VERSION

Ви зауважите, що з Bash перший рядок виводить вашу версію, а другий ніколи нічого не говорить, незалежно від того, яку оболонку ви використовуєте.

  • Якщо ваш /bin/sh, скажімо,, dashто жоден рядок нічого не виведе, коли сценарій виконується з zsh або тире.
  • Якщо ваше /bin/shпосилання на Bash, ви побачите вихід першого рядка у всіх випадках.
  • Якщо /bin/shце інша версія Баша , ніж ви використовували безпосередньо, ви побачите інший висновок при запуску сценарію з Баш безпосередньо і від Zsh.

ps -p $$Команда від відповіді рулетиков ігрового також покаже корисну інформацію про команду оболонки , використовуваної для виконання сценарію.


Однак я схвалив, що він використовує DEFAULT SHELL, якщо не вказано шебанг, незалежно від того, яку оболонку за замовчуванням ви вказали. Ось чому завжди слід, імхо, вказувати шебанг.
thecarpy

4
Це не так, в чому насправді була вся суть відповіді. Ваше друге речення, безумовно, правдиве.
Майкл Гомер

Ви маєте рацію, bash запускає його, використовуючи себе, оболонка за замовчуванням у більшості моїх скриньок буває .... баш, звідти моя плутанина. Я щойно тестував на Solaris 8 з ksh як оболонку за замовчуванням, напевно, bash запускає це як bash ... сьогодні дізнався щось нове, дякую (прихильне!) ;-)
thecarpy

Спасибі. Чи відбувається збій execl()виклику, коли скрипт оболонки не містить шебанг і виконується як scriptname? Хіба це не відбувається, коли сценарій оболонки виконується як bash scriptname? Чи не трапляється, коли скрипт оболонки містить шебанг і виконується як scriptname?
StackExchange для всіх


7

Оскільки файл не має жодного з типів виконуваного файлу, розпізнаваного системою, і якщо припустити, що у вас є дозвіл на виконання цього файлу, execve()системний виклик, як правило, не працює з помилкою ENOEXEC( не виконуваною ).

Тоді, що станеться, залежить від програми та / або функції бібліотеки, яка використовується для виконання команди.

Це може бути, наприклад, оболонка, функція execlp()/ execvp()libc.

Більшість інших додатків використовуватимуть будь-який із них, коли вони виконують команду. Вони будуть викликати оболонку наприклад , за допомогою шляхом system("command line")LibC функції , яка зазвичай викликається shдля розбору цієї командного рядка (шлях , який може бути визначений під час компіляції (наприклад , /bin/shпроти /usr/xpg4/bin/shна Solaris)), або запускайте оболонки зберігається в $SHELLсамому по собі , як viз його !командою або xterm -e 'command line'та багатьма іншими командами ( su user -cзамість них буде викликати оболонку входу користувача $SHELL).

Як правило, текстовий файл без шебангу, який не починається з #, вважається shсценарієм. Який shвін буде різним, хоча.

execlp()/ execvp(), після execve()повернення ENOEXECзазвичай посилається shна нього. Для систем, які мають більше однієї, shоскільки вони можуть відповідати більш ніж одному стандарту, який sh, як правило, визначається під час компіляції (програми, що використовує execvp()/ execlp()прив'язуючи інший фрагмент коду, який посилається на інший шлях sh). Наприклад, для Solaris це буде або /usr/xpg4/bin/sh(стандарт, POSIX sh), або /bin/sh(оболонка Bourne (антикваріальна оболонка) для Solaris 10 і старші, ksh93 в Solaris 11).

Що стосується снарядів, тут багато варіацій. bash, AT&T ksh, оболонка Bourne зазвичай інтерпретує сам сценарій (у дочірньому процесі, якщо execне використовується) після імітації a execve(), тобто скидання всіх експортованих змінних, закриття всіх FDS-файлів close-on-exec, видалення всіх спеціальних пасток, псевдоніми, функції ... ( bashбуде інтерпретувати сценарій у shрежимі). yashвиконає себе (з shтаким argv[0]чином у shрежимі), щоб інтерпретувати його.

zsh, pdksh, ash-А оболонка зазвичай викликається sh(шлях якого визначається під час компіляції).

Для cshі tcsh(та shдеяких ранніх BSD), якщо перший символ файлу є #, то вони самі виконають його інтерпретацію та shінше. Це повертається до часів перед шебангом, коли cshвизнавали #коментарі, але не оболонкою Борна, тому #було натяком на те, що це сценарій csh.

fish(принаймні версія 2.4.0), просто повертає помилку, якщо execve()не вдається (вона не намагається розглянути її як сценарій).

Деякі оболонки (наприклад, bashAT & T ksh) спершу спробують евристично визначити, чи мається на увазі, що файл - це сценарій чи ні. Таким чином, ви можете виявити, що деякі оболонки відмовляться виконувати сценарій, якщо він отримав символ NUL в перших кількох байтах.

Також зауважте, що якщо execve()помилка ENOEXEC, але у файлу є лінія shebang, деякі оболонки намагаються самостійно інтерпретувати цю лінію shebang.

Ось кілька прикладів:

  • Коли $SHELLє /bin/bash, xterm -e 'myscript with args'буде myscriptінтерпретуватися bashв shрежимі. У той час як з xterm -e myscript with args, xtermвикористовуватиме, execvp()тому сценарій буде інтерпретований sh.
  • su -c myscriptна Solaris 10, де rootоболонка входу є /bin/shі /bin/shоболонка Борна, буде myscriptінтерпретована оболонкою Борна.
  • /usr/xpg4/bin/awk 'BEGIN{system("myscript")'на Solaris 10 буде інтерпретовано це /usr/xpg4/bin/sh(те саме для /usr/xpg4/bin/env myscript).
  • find . -prune -exec myscript {} \;на Solaris 10 (використовуючи execvp()) буде інтерпретувати це /bin/shнавіть з /usr/xpg4/bin/find, навіть у середовищі POSIX (помилка відповідності).
  • csh -c myscriptбуде інтерпретовано, cshякщо це починається з #, shінакше.

Загалом, ви не можете бути впевнені, яка оболонка буде використовуватися для інтерпретації цього сценарію, якщо ви не знаєте, як і на що він буде викликаний.

У будь-якому випадку read -pце bashсинтаксис -inly, тому ви хочете переконатися, що сценарій інтерпретується bash(і уникайте цього оманливого .shрозширення). Або ви знаєте шлях bashвиконуваного файлу та використовуйте:

#! /path/to/bash -
read -p ...

Або ви можете спробувати покластися на $PATHпошук bashвиконуваного файлу (при умові bash, що встановлено), використовуючи:

#! /usr/bin/env bash
read -p ...

( envмайже всюди зустрічається в /usr/bin). Крім того, ви можете зробити POSIX + Bourne сумісним, і в цьому випадку ви можете їх використовувати /bin/sh. Усі системи матимуть /bin/sh. У більшості з них це буде (здебільшого) POSIX-сумісний, але ви все ще можете знайти тут і потім оболонку Bourne.

#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"

1

Якщо у вас немає жодної #!(званої шебанг ) лінії, використовується ш . Щоб перевірити це, ви можете запустити наступний сценарій.

ps -p $$
echo -n "The real shell is: "
realpath /proc/$$/exe

На комп’ютер я заходжу

  PID TTY          TIME CMD
13718 pts/16   00:00:00 sh
The real program is: /usr/bin/bash

навіть якщо моя оболонка за замовчуванням zsh . Він використовує bash, оскільки на моїй машині команда sh реалізована bash .


1
Чому ти спротив це? Поясніть, будь ласка, чи це марно ...
роли

Ненависників (мовчазних анонімних прихильників) ненавидять. Я щось дізнався з вашої відповіді, тож ось підсумок. :-)
Мак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.