Выполнение Bash-скрипта с и без шебанга в Linux и BSD - PullRequest
17 голосов
/ 01 сентября 2011

Как и кто определяет, что выполняется, когда Bash-подобный скрипт выполняется как двоичный файл без шебанга?

Я полагаю, что при запуске нормального скрипта с шебанг обрабатывается с помощью binfmt_script Модуль Linux, который проверяет shebang, анализирует командную строку и запускает назначенный интерпретатор сценариев.

Но что происходит, когда кто-то запускает сценарий без shebang?Я проверил прямой execv подход и обнаружил, что в нем нет магии ядра - то есть такой файл:

$ cat target-script
echo Hello
echo "bash: $BASH_VERSION"
echo "zsh: $ZSH_VERSION"

Запуск скомпилированной программы на C, которая выполняет только вызов execv, дает:

$ cat test-runner.c
void main() {
        if (execv("./target-script", 0) == -1)
                perror();
}
$ ./test-runner
./target-script: Exec format error

Однако, если я делаю то же самое из другого сценария оболочки, он запускает целевой сценарий, используя тот же интерпретатор оболочки, что и исходный:

$ cat test-runner.bash
#!/bin/bash
./target-script

$ ./test-runner.bash 
Hello
bash: 4.1.0(1)-release
zsh: 

Если я выполняюТот же трюк с другими оболочками (например, по умолчанию в Debian sh - /bin/dash), он также работает:

$ cat test-runner.dash   
#!/bin/dash
./target-script

$ ./test-runner.dash 
Hello
bash: 
zsh: 

Таинственным образом, он не совсем работает, как ожидается, с zsh и не следуетобщая схема.Похоже, zsh в конце концов выполнил /bin/sh для таких файлов:

greycat@burrow-debian ~/z/test-runner $ cat test-runner.zsh 
#!/bin/zsh
echo ZSH_VERSION=$ZSH_VERSION
./target-script

greycat@burrow-debian ~/z/test-runner $ ./test-runner.zsh 
ZSH_VERSION=4.3.10
Hello
bash: 
zsh: 

Обратите внимание, что ZSH_VERSION в родительском скрипте работал, в то время как ZSH_VERSION в дочернем не работал!

Как это работаетоболочка (Bash, Dash) определяет, что будет выполнено, когда нет шебанга?Я пытался откопать это место в источниках Bash / Dash, но, увы, похоже, я там немного заблудился.Может ли кто-нибудь пролить свет на магию, которая определяет, должен ли целевой файл без shebang быть выполнен как скрипт или как двоичный файл в Bash / dash?Или, может быть, это какое-то взаимодействие с ядром / libc, и тогда я хотел бы получить объяснения о том, как это работает в ядрах / libcs ​​Linux и FreeBSD?

Ответы [ 2 ]

16 голосов
/ 01 сентября 2011

Поскольку это происходит в тире, а тире проще, я сначала посмотрел.

Похоже, exec.c - это место для поиска, а соответствующие функции - tryexec, который вызывается из shellexec, который вызывается всякий раз, когда оболочка создает команду, которую необходимо выполнить. И (упрощенная версия) функция tryexec выглядит следующим образом:

STATIC void
tryexec(char *cmd, char **argv, char **envp)
{
        char *const path_bshell = _PATH_BSHELL;

repeat:

        execve(cmd, argv, envp);

        if (cmd != path_bshell && errno == ENOEXEC) {
                *argv-- = cmd;
                *argv = cmd = path_bshell;
                goto repeat;
        }
}

Таким образом, он просто всегда заменяет команду для выполнения на путь к себе (_PATH_BSHELL по умолчанию "/bin/sh"), если происходит ENOEXEC. Здесь действительно нет магии.

Я считаю, что FreeBSD демонстрирует идентичное поведение в bash и в своем собственном sh.

То, как bash справляется с этим, похоже, но гораздо сложнее. Если вы хотите посмотреть дальше, я рекомендую прочитать bash's execute_command.c и обратить особое внимание на execute_shell_script, а затем shell_execve. Комментарии довольно наглядны.

9 голосов
/ 01 сентября 2011

(Похоже, что Сорпигаль это уже рассмотрел, но я уже напечатал это, и это может быть интересно.)

Согласно разделу 3.16 FAQ по Unix , оболочка сначала просматривает магическое число (первые два байта файла). Некоторые числа указывают двоичный исполняемый файл; #! указывает, что остальная часть строки должна интерпретироваться как шебанг. В противном случае оболочка пытается запустить его как сценарий оболочки.

Кроме того, кажется, что csh смотрит на первый байт, и если он #, он попытается запустить его как csh скрипт.

...