Что делает команда «exec»?

107

Я не понимаю команду bash exec. Я видел, как он используется внутри скриптов, чтобы перенаправить весь вывод в файл (как видно из этого ). Но я не понимаю, как это работает или что вообще делает. Я прочитал справочные страницы, но я их не понимаю.

becko
источник
Вы знакомы с процессом ?
fkraiem
1
@fkraiem что ты имеешь ввиду?
Бекко
Извините, я имел в виду "что". : p Но ответ, похоже, нет.
фкрем
Но на самом деле ваш сценарий использует execособый способ, который можно объяснить гораздо проще, я напишу ответ.
fkraiem
1
Связанные: unix.stackexchange.com/q/296838/85039
Сергей Колодяжный

Ответы:

88

man bash говорит:

exec [-cl] [-a name] [command [arguments]]
      If command is specified, it replaces the shell.  No new  process
      is  created.  The arguments become the arguments to command.  If
      the -l option is supplied,  the  shell  places  a  dash  at  the
      beginning  of  the  zeroth  argument passed to command.  This is
      what login(1) does.  The -c option causes command to be executed
      with  an empty environment.  If -a is supplied, the shell passes
      name as the zeroth argument to the executed command.  If command
      cannot  be  executed  for  some  reason, a non-interactive shell
      exits, unless the execfail shell option  is  enabled.   In  that
      case,  it returns failure.  An interactive shell returns failure
      if the file cannot be executed.  If command  is  not  specified,
      any  redirections  take  effect  in  the  current shell, and the
      return status is 0.  If there is a redirection error, the return
      status is 1.

Последние две строки - вот что важно: если вы запустите execсам, без команды, это просто заставит перенаправления применяться к текущей оболочке. Вы, вероятно, знаете, что когда вы запускаете command > file, вывод commandзаписывается fileвместо вашего терминала (это называется перенаправлением ). Если вы запускаете exec > fileвместо этого, то перенаправление применяется ко всей оболочке: любой вывод, произведенный оболочкой, записывается fileвместо вашего терминала. Например здесь

bash-3.2$ bash
bash-3.2$ exec > file
bash-3.2$ date
bash-3.2$ exit
bash-3.2$ cat file
Thu 18 Sep 2014 23:56:25 CEST

Я сначала запускаю новую bashоболочку. Затем в этой новой оболочке я запускаю exec > file, так что весь вывод перенаправляется в file. Действительно, после этого я бегу, dateно я не получаю вывод, потому что вывод перенаправлен в file. Затем я выхожу из своей оболочки (так что перенаправление больше не применяется), и я вижу, что fileдействительно содержит вывод dateкоманды, которую я выполнил ранее.

fkraiem
источник
42
Это только частичное объяснение. execслужит также для замены текущего процесса оболочки командой, так что родительский процесс идет своим путем, а дочерний владеет pid. Это не только для перенаправления. Пожалуйста, добавьте эту информацию
Сергей Колодяжный
3
После комментария @ SergiyKolodyazhnyy, я только что столкнулся с примером, который привел меня на эту страницу, - docker-entrypoint.sh, где после выполнения различных действий в конфигурации nginx последняя строка скрипта - это exec nginx <various nginx arguments>. Это означает, что nginx принимает pid скрипта bash, и теперь nginx является основным запущенным процессом контейнера, а не скриптом. Я предполагаю, что это только для чистоты, если кто-то еще не знает более конкретную причину для этого?
Люк Гриффитс
1
@Luke Это называется «скрипт-обертка». Пример тому также gnome-terminal, который по крайней мере 14.04 имел скрипт-обертку для установки аргументов. И это их единственная цель, на самом деле - установить искусство и окружающую среду. Другой случай - очистить - сначала убить предыдущий процесс и запустить новый
Сергей Колодяжный
Другой execпример, аналогичный nginxпримеру, приведенному @LukeGriffiths, - это ~/.vnc/xstartupсценарий, который vncserverиспользуется для настройки процесса сервера VNC, а затем exec gnome-sessionили exec startkdeи так далее.
Тревор Бойд Смит
@LukeGriffiths, основная причина execв скрипте запуска контейнера состоит в том, что PID 1, ENTRYPOINT контейнера, имеет особое значение в Docker. Это основной процесс, который получает сигналы, и когда он существует, контейнер тоже выходит. execпросто способ вывести shиз этой цепочки команд и сделать демон главным процессом контейнера.
ккм
45

exec это команда с двумя очень разными поведениями, в зависимости от того, используется ли с ней хотя бы один аргумент или вообще не используется ни один аргумент.

  • Если передан хотя бы один аргумент, первый принимается за имя команды и execпытается выполнить его как команду, передавая оставшиеся аргументы, если таковые имеются, этой команде и управляя перенаправлениями, если они есть.

  • Если команда, переданная в качестве первого аргумента, не существует, текущая оболочка, а не только команда exec, завершается с ошибкой.

  • Если команда существует и является исполняемой, она заменяет текущую оболочку. Это означает, что если execв скрипте появляется, инструкции, следующие за вызовом exec , никогда не будут выполнены (если только он не execнаходится в подоболочке). execникогда не вернется Оболочечные ловушки типа «EXIT» тоже не сработают.

  • Если аргумент не передан, execиспользуется только для переопределения текущих дескрипторов файла оболочки. Оболочка продолжается после exec, в отличие от предыдущего случая, но вступают в силу стандартный ввод, вывод, ошибка или любой дескриптор файла, который был перенаправлен.

  • Если некоторые из перенаправлений используют /dev/null, любой вход от него будет возвращать EOF, а любой выход для него будет отброшен.

  • Вы можете закрыть файловые дескрипторы, используя в -качестве источника или места назначения, например exec <&-. Последующее чтение или запись потерпит неудачу.

Вот два примера:

echo foo > /tmp/bar
exec < /tmp/bar # exec has no arguments, will only affect current shell descriptors, here stdin
cat # simple command that read stdin and write it to stdout

Этот скрипт выведет «foo» в качестве команды cat, вместо того, чтобы ждать ввода пользователя, как это было бы в обычном случае, и получит свой ввод из файла / tmp / bar, который содержит foo.

echo foo > /tmp/bar
exec wc -c < /tmp/bar # exec has two arguments, the control flow will switch to the wc command
cat

Этот скрипт отобразит 4(количество байтов в / tmp / bar) и сразу же завершится. Команда catне будет выполнена.

jlliagre
источник
4
`Некоторые старые сообщения никогда не стареют ... +1.
Cbhihe
3
Если для некоторых перенаправлений используется / dev / null, соответствующий дескриптор файла закрывается. Нет, он действительно перенаправляет в / из /dev/null, поэтому запись по-прежнему успешна, а чтение возвращает EOF. close(2)на FD вызовет системные вызовы чтения / записи, чтобы вернуть ошибки, и вы делаете это, exec 2>&-например, с.
Питер Кордес
2
Попробуйте сами:: exec 3</dev/null; ls -l /proc/self/fdобратите внимание, что fd 3 будет открыт только для чтения в / dev / null. Затем закройте его снова exec 3<&-, и вы увидите ( ls -l /proc/$$/fdснова), что у вашего процесса оболочки больше нет fd 3. (Закрытие стандартного ввода exec <&-может быть полезно в сценариях, но в интерактивном режиме это выход из системы.)
Питер Кордес
@PeterCordes Вы абсолютно правы. Ответ обновлен. Спасибо!
17
1
Этот ответ хорош, потому что он объясняет два варианта execиспользования текущего ответа с наибольшим количеством голосов говорит только об одном варианте использования, а другой ответ g_p говорит только о другом варианте использования. И этот ответ хорош и лаконичен / читабелен для такой сложной темы.
Тревор Бойд Смит
33

Чтобы понять exec, нужно сначала понять fork. Я стараюсь быть коротким.

  • Когда вы выходите на развилку дороги, у вас обычно есть два варианта. Программы Linux достигают этой развилки в дороге, когда сталкиваются с fork()системным вызовом.

  • Обычные программы - это системные команды, которые существуют в скомпилированной форме в вашей системе. Когда такая программа выполняется, создается новый процесс. Этот дочерний процесс имеет ту же среду, что и его родительский процесс, отличается только идентификационный номер процесса. Эта процедура называется разветвлением .

  • Форкинг позволяет существующему процессу начать новый. Однако могут быть ситуации, когда дочерний процесс не является частью той же программы, что и родительский процесс. В этом случае execиспользуется. exec заменит содержимое текущего запущенного процесса информацией из двоичного файла программы.
  • После процесса разветвления адресное пространство дочернего процесса перезаписывается новыми данными процесса. Это делается через вызов exec системы.
G_p
источник
3
Вы можете объяснить, почему execможно перенаправить вывод скрипта, как в ссылке, которую я разместил?
Бекко
1
Я обнаружил, что это неясно «Однако могут быть ситуации, когда дочерний процесс не является частью той же программы, что и родительский процесс»
cdosborn
6

В bash, если вы делаете help exec:

$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
    Replace the shell with the given command.

    Execute COMMAND, replacing this shell with the specified program.
    ARGUMENTS become the arguments to COMMAND.  If COMMAND is not specified,
    any redirections take effect in the current shell.

    Options:
      -a name   pass NAME as the zeroth argument to COMMAND
      -c        execute COMMAND with an empty environment
      -l        place a dash in the zeroth argument to COMMAND

    If the command cannot be executed, a non-interactive shell exits, unless
    the shell option `execfail' is set.

    Exit Status:
    Returns success unless COMMAND is not found or a redirection error occurs.

Соответствующий бит:

If COMMAND is not specified, any redirections take effect in the current shell.

execявляется встроенной оболочкой , которая является оболочкой, эквивалентной execсемейству системных вызовов, о которых говорит G_P (и чьи страницы управления, по-видимому, вы прочитали). Он имеет только обязательную функциональность POSIX, влияющую на текущую оболочку, если команда не указана.

Мур
источник