Когда я запускаю этот скрипт, намеревался запускать, пока не убил
# foo.sh
while true; do sleep 1; done
... Я не могу найти его с помощью ps ax
:
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21110 pts/3 S+ 0:00 grep --color=auto foo.sh
... но если я просто добавлю общий " #!
" заголовок к сценарию ...
#! /usr/bin/bash
# foo.sh
while true; do sleep 1; done
... тогда сценарий становится доступным для этой же ps
команды ...
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21319 pts/43 S+ 0:00 /usr/bin/bash ./foo.sh
21324 pts/3 S+ 0:00 grep --color=auto foo.sh
Почему это так?
Это может быть связанный вопрос: я подумал, что " #
" это просто префикс комментария, а если так " #! /usr/bin/bash
" сам по себе не более, чем комментарий. Но имеет ли " #!
" какое-то значение больше, чем просто комментарий?
shell-script
shell
ps
shebang
StoneThrow
источник
источник
Ответы:
Когда текущая интерактивная оболочка есть
bash
, и вы запускаете сценарий без#!
-line, тогдаbash
будет запускаться сценарий. Процесс будет отображаться наps ax
выходе простоbash
.В другом терминале:
Связанный:
Соответствующие разделы составляют
bash
руководство:Это означает, что выполнение
./foo.sh
в командной строке, когдаfoo.sh
нет строки,#!
аналогично выполнению команд в файле в подоболочке, то естьС правильной
#!
линией, указывающей, например/bin/bash
, это какисточник
ps
показывает, что скрипт работает как "/usr/bin/bash ./foo.sh
". Таким образом, в первом случае, как вы говорите, bash запустит скрипт, но разве этот сценарий не нужно будет «передавать» в разветвленный исполняемый файл bash, как во втором случае? (и если это так, я думаю, что это можно было бы найти с помощью трубы, чтобы grep ...?)$$
все еще указывает на старый в случае подоболочки (echo $BASHPID
/bash -c 'echo $PPID'
).Когда сценарий оболочки начинается с
#!
этой первой строки, это комментарий в отношении оболочки. Однако первые два символа имеют значение для другой части системы: ядра. Два персонажа#!
называются шебанг . Чтобы понять роль шебанга, вам нужно понять, как выполняется программа.Выполнение программы из файла требует действий от ядра. Это делается как часть
execve
системного вызова. Ядро должно проверить права доступа к файлу, освободить ресурсы (память и т. Д.), Связанные с исполняемым файлом, который в данный момент выполняется в вызывающем процессе, выделить ресурсы для нового исполняемого файла и передать управление новой программе (и другим вещам, которые Я не буду упоминать).execve
Системный вызов заменяет код процесса в настоящее время работает; есть отдельный системный вызовfork
для создания нового процесса.Для этого ядро должно поддерживать формат исполняемого файла. Этот файл должен содержать машинный код, организованный так, чтобы его понимало ядро. Сценарий оболочки не содержит машинный код, поэтому он не может быть выполнен таким образом.
Механизм shebang позволяет ядру отложить задачу интерпретации кода в другой программе. Когда ядро видит, что исполняемый файл начинается с
#!
, оно читает следующие несколько символов и интерпретирует первую строку файла (без начального#!
и необязательного пробела) как путь к другому файлу (плюс аргументы, которые я не буду здесь обсуждать) ). Когда ядру говорят выполнить файл/my/script
, и он видит, что файл начинается со строки#!/some/interpreter
, ядро выполняется/some/interpreter
с аргументом/my/script
. Затем/some/interpreter
нужно решить, что/my/script
это файл сценария, который он должен выполнить.Что если файл не содержит нативный код в формате, понятном ядру, и не начинается с шебанга? Что ж, тогда файл не является исполняемым, и
execve
системный вызов завершается ошибкой с кодом ошибкиENOEXEC
(ошибка формата исполняемого файла).Это может быть концом истории, но большинство оболочек реализуют запасную функцию. Если ядро возвращается
ENOEXEC
, оболочка просматривает содержимое файла и проверяет, выглядит ли он как сценарий оболочки. Если оболочка считает, что файл выглядит как сценарий оболочки, она выполняет его сама. Детали того, как это происходит, зависят от оболочки. Вы можете увидеть кое-что из того, что происходит, добавивps $$
в свой скрипт, и многое другое, наблюдая за процессом,strace -p1234 -f -eprocess
где 1234 - это PID оболочки.В bash этот резервный механизм реализован путем вызова,
fork
но неexecve
. Дочерний процесс bash самостоятельно очищает свое внутреннее состояние и открывает новый файл сценария для его запуска. Поэтому процесс, выполняющий сценарий, все еще использует исходное изображение кода bash и исходные аргументы командной строки, переданные при первоначальном вызове bash. ATT ksh ведет себя так же.Dash, напротив, реагирует на это
ENOEXEC
, вызывая/bin/sh
путь к сценарию, переданному в качестве аргумента. Другими словами, когда вы выполняете сценарий без shebang из dash, он ведет себя так, как будто сценарий имеет строку shebang#!/bin/sh
. Мкш и зш ведут себя одинаково.источник
bash
разветвлен, он имеет доступ к тому жеargv[]
массиву, что и его родительский элемент, и именно так он узнает «исходные аргументы командной строки, переданные при первоначальном вызове bash», и если вот почему ребенок не передал исходный сценарий в качестве явного аргумента (следовательно, почему он не может быть найден grep) - это точно?BINFMT_SCRIPT
модуль управляет этим и может быть удален / модульно, хотя обычно он статически связан с ядром), но я не понимаю, почему вы захотите, кроме, возможно, во встроенной системе , В качестве обходного пути для этой возможностиbash
есть флаг конфигурации (HAVE_HASH_BANG_EXEC
) для компенсации!ps
сообщает как аргументы командной строки, но только до определенного момента: она должна изменить существующий буфер памяти, она не может увеличить этот буфер. Так что, если bash попытается изменить его,argv
добавив имя скрипта, это не всегда будет работать. Дочерний объект не «передал аргумент», потому что у него никогда не бываетexecve
системного вызова. Это все тот же образ процесса bash, который продолжает работать.В первом случае скрипт запускается раздвоенным потомком из вашей текущей оболочки.
Сначала вы должны запустить,
echo $$
а затем взглянуть на оболочку, в которой идентификатор процесса вашей оболочки является идентификатором родительского процесса.источник