Какой интерпретатор оболочки запускает скрипт без шебанга?


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?

** РЕДАКТИРОВАТЬ: ** Вот содержание сценария

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 или dash.
  • Если ваша /bin/shссылка на Bash, вы увидите вывод первой строки во всех случаях.
  • Если /bin/shэто другая версия Баша , чем вы использовали напрямую, вы увидите другой вывод при запуске сценария из Баш непосредственно и от Zsh.

Команда ps -p $$из ответа rools также покажет полезную информацию о команде, используемой оболочкой для выполнения скрипта.


Я проголосовал, однако, он использует DEFAULT SHELL, если не указан shebang, независимо от того, какую оболочку по умолчанию вы указали. Вот почему вы всегда должны указывать шебанг.
thecarpy

4
Это не так, и в этом весь смысл ответа. Ваше второе предложение, безусловно, верно.
Майкл Гомер

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

Благодарю. Происходит ли сбой execl()вызова, когда сценарий оболочки не содержит шебанга и выполняется как scriptname? Разве это не происходит, когда скрипт оболочки выполняется как bash scriptname? Разве это не происходит, когда скрипт оболочки содержит шебанг и выполняется как scriptname?
StackExchange для всех


7

Поскольку файл не относится ни к одному из типов исполняемых файлов, распознаваемых системой, и при условии, что у вас есть разрешение на выполнение этого файла, execve()системный вызов обычно завершается с ошибкой ENOEXEC( не исполняемой ).

То, что происходит затем, зависит от приложения и / или библиотечной функции, используемой для выполнения команды.

Это может быть, например, оболочка execlp()/execvp() функция libc.

Большинство других приложений будут использовать любое из них при запуске команды. Они будут вызывать оболочку, например, с помощью system("command line")функции libc, которая обычно вызывает shсинтаксический анализ этой командной строки (путь которой может быть определен во время компиляции (как /bin/shvs /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).

Когда дело доходит до раковин, есть много вариаций. bashAT & T ksh, оболочка Bourne обычно интерпретирует скрипт (в дочернем процессе, если он execне используется) после того, как имитировал execve(), который сбрасывает все неэкспортированные переменные, закрывает все fd-команды close-on-exec, удаляет все пользовательские ловушки, псевдонимы, функции ... ( bashбудет интерпретировать скрипт в shрежиме). yashвыполнит себя ( shкак argv[0]в shрежиме), чтобы интерпретировать его.

zsh, pdksh, ash-А оболочка обычно вызывается sh(путь которого определяется во время компиляции).

Для cshи tcshshнекоторых ранних BSD), если первым символом файла является #, то они выполнятся сами для его интерпретации, и в shпротивном случае. Это восходит к периоду до Шебанга, когда cshон распознавал #как комментарии, но не оболочку Bourne, так что #был намек на то, что это был скрипт 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является оболочкой Bourne, будет myscriptинтерпретироваться оболочкой Bourne.
  • /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синтаксис, поэтому вы должны убедиться, что скрипт интерпретируется 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

Если у вас нет какой-либо #!(называемой шебанг ) строки, используется sh . Чтобы проверить это, вы можете запустить следующий скрипт.

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
Почему вы это понизили? Пожалуйста, объясните, или это бесполезно ...
Рул

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