Intereting Posts
Linux Live CD не может воспроизводить видео Масштабирование HiDPI для приложений, которые не знакомы gnuplot – «подталкивать» отложенные точки от края диаграммы Установка файла bz2 Почему эта переменная среды не настроена? ersync -compare-dest arg не существует независимо от того, что Что произойдет, если я установил уровень запуска системы по умолчанию на 0 или 6? Состояние потоков и статус процесса Как использовать redobackup без свободного раздела Включите параллельный порт и используйте его для последовательной связи (RS-232) Домашний каталог для пользователей системы NetworkManager: установить текущее соединение устройства как измеренное Простая команда AWK для проверки правильности написания пути к каталогу Существует ли пакет Linux в Ubuntu, который предлагает инструмент sendmail, который может пересылать сообщение с SMTP или резервный файл в файл при сбое SMTP? Изменение шрифтов для приложения Smuxi

Почему «ps ax» не находит работающий скрипт bash без заголовка «#!»?

Когда я запускаю этот скрипт, намеревался запускать, пока не убил …

# 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 bash , и вы запускаете скрипт без #! -line, тогда bash запустит скрипт. Процесс будет отображаться в выводе ps ax как просто bash .

 $ cat foo.sh # foo.sh echo "$BASHPID" while true; do sleep 1; done $ ./foo.sh 55411 

В другом терминале:

 $ ps -p 55411 PID TT STAT TIME COMMAND 55411 p2 SN+ 0:00.07 bash 

Связанные с:

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

Соответствующие разделы составляют руководство по bash :

Если это выполнение не выполнено из-за того, что файл не в исполняемом формате и файл не является каталогом, предполагается, что это сценарий оболочки , файл, содержащий команды оболочки. Для его запуска создается подshell . Эта подshell переинициализирует себя так, что эффект выглядит так, как будто новая shell была вызвана для обработки сценария , за исключением того, что местоположения команд, запомненных родителем (см. Хэш ниже в разделе «Команды SHELL BUILTIN»), остаются дочерними.

Если программа представляет собой файл, начинающийся с #! , остаток от первой строки указывает интерпретатор для программы. Оболочка выполняет указанный интерпретатор в операционных системах, которые сами не обрабатывают этот исполняемый формат. […]

Это означает, что ./foo.sh в командной строке, когда foo.sh не имеет #! -line, аналогичен выполнению команд в файле в подоболочке, т.е.

 $ ( echo "$BASHPID"; while true; do sleep 1; done ) 

С правильным #! -линии, указывающие, например, на /bin/bash , это как

 $ /bin/bash foo.sh 

Когда скрипт оболочки начинается с #! Эта первая строка является комментарием в отношении оболочки. Однако первые два символа имеют значение для другой части системы: ядра. Два персонажа #! называются шебанг . Чтобы понять роль шебанга, вам нужно понять, как выполняется программа.

Выполнение программы из файла требует действий со стороны ядра. Это делается как часть execve вызова execve . Ядро должно проверить права доступа к файлу, освободить ресурсы (память и т. Д.), Связанные с исполняемым файлом, который в данный момент выполняется в вызывающем процессе, выделить ресурсы для нового исполняемого файла и передать управление новой программе (и другим вещам, которые Я не буду упоминать). Системный вызов execve заменяет код текущего запущенного процесса; есть отдельный системный вызов fork для создания нового процесса.

Для этого kernel ​​должно поддерживать формат исполняемого файла. Этот файл должен содержать машинный код, организованный так, чтобы его понимало kernel. Сценарий оболочки не содержит машинный код, поэтому он не может быть выполнен таким образом.

Механизм shebang позволяет ядру отложить задачу интерпретации кода в другой программе. Когда kernel ​​видит, что исполняемый файл начинается с #! , он читает следующие несколько символов и интерпретирует первую строку файла (за исключением начального #! и необязательного пробела) как путь к другому файлу (плюс аргументы, которые я не буду здесь обсуждать). Когда ядру предписывается выполнить файл /my/script и он видит, что файл начинается со строки #!/some/interpreter , kernel ​​выполняет /some/interpreter с аргументом /my/script . Затем до /some/interpreter может решить, что /my/script – это файл сценария, который он должен выполнить.

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

Это может быть концом истории, но большинство оболочек реализуют запасную функцию. Если kernel ​​возвращает ENOEXEC , shell просматривает содержимое файла и проверяет, выглядит ли он как скрипт оболочки. Если shell считает, что файл выглядит как сценарий оболочки, она выполняет его сама. Детали того, как это происходит, зависят от оболочки. Вы можете увидеть, что происходит, добавив ps $$ в ваш скрипт, а еще больше, наблюдая за процессом с помощью strace -p1234 -f -eprocess где 1234 – PID оболочки.

В bash этот резервный механизм реализуется путем вызова fork но не execve . Дочерний процесс bash самостоятельно очищает свое внутреннее состояние и открывает новый файл сценария для его запуска. Поэтому процесс, выполняющий сценарий, все еще использует исходное изображение кода bash и исходные аргументы командной строки, переданные при первоначальном вызове bash. ATT ksh ведет себя так же.

 % bash --norc bash-4.3$ ./foo.sh PID TTY STAT TIME COMMAND 21913 pts/2 S+ 0:00 bash --norc 

Dash, напротив, реагирует на ENOEXEC , вызывая /bin/sh с путем к сценарию, переданному в качестве аргумента. Другими словами, когда вы выполняете сценарий без шебанга из тире, он ведет себя так, как будто сценарий имеет строку шебанга с #!/bin/sh . Мкш и зш ведут себя одинаково.

 % dash $ ./foo.sh PID TTY STAT TIME COMMAND 21427 pts/2 S+ 0:00 /bin/sh ./foo.sh 

В первом случае скрипт запускается раздвоенным потомком из вашей текущей оболочки.

Сначала вы должны запустить echo $$ а затем взглянуть на оболочку, в которой идентификатор процесса вашей оболочки является идентификатором родительского процесса.