В чем разница между eval и exec?

Я бы использовал оболочку для этого используется функция , а не скрипт:

rm-all-or-none() {
  for f; do
    [ -f "$f" ] ||
      { printf '%s is not an existing file, no files removed\n' "$f" >&2
        return 1;}
  done
  rm -fv -- "$@"
}

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

rm-all-or-none /tmp/bbsnode{1..4}

Более длинная эквивалентная версия:

rm-all-or-none() {
  for f in "$@"; do
    if [ -f "$f" ]; then
      :
    else
      printf '%s is not an existing file, no files removed\n' "$f" >&2
      return 1
    fi
  done
  rm -fv -- "$@"
}

Также см .:

83
08.03.2017, 18:24
5 ответов

eval и exec - совершенно разные звери. (Кроме того факта, что оба выполняют команды, но и все, что вы делаете в оболочке)

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

То, что делает exec cmd, точно такое же, как просто запуск cmd, за исключением того, что текущая оболочка заменяется командой, а не запускается отдельный процесс. Внутренне, запуск, скажем, /bin/ls вызовет fork() для создания дочернего процесса, а затем exec() в дочернем процессе для выполнения /bin/ls. exec /bin/ls, с другой стороны, не форк, а просто заменяет оболочку.

Сравните:

$ bash -c 'echo $$ ; ls -l /proc/self ; echo foo'
7218
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7219
foo

с

$ bash -c 'echo $$ ; exec ls -l /proc/self ; echo foo'
7217
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7217

echo $$ выводит PID запущенной оболочки, а листинг /proc/self дает нам PID ls, который был запущен из оболочки. Обычно идентификаторы процессов отличаются, но в случае с exec shell и ls имеют один и тот же идентификатор процесса. Кроме того, команда, следующая за exec, не выполнялась, поскольку оболочка была заменена.


С другой стороны:

$ help eval
eval: eval [arg ...]
    Execute arguments as a shell command.

eval запустит аргументы как команду в текущей оболочке. Другими словами, eval foo bar - это то же самое, что просто foo bar. Но переменные будут расширены перед выполнением, поэтому мы можем выполнять команды, сохраненные в переменных оболочки:

$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo

Это не создаст дочерний процесс, поэтому переменная устанавливается в текущей оболочке. (Конечно, eval /bin/ls создаст дочерний процесс, так же как и старый добрый /bin/ls.)

Или мы можем иметь команду, которая выводит команды оболочки. Выполнение ssh-agent запускает агента в фоновом режиме и выводит кучу назначений переменных, которые могут быть установлены в текущей оболочке и использованы дочерними процессами (команды ssh, которые вы бы запустили). Следовательно, ssh-agent может быть запущен с помощью:

eval $(ssh-agent)

И текущая оболочка получит переменные для наследования другими командами.


Конечно, если переменная cmd будет содержать что-то вроде rm -rf $HOME, то выполнение eval "$cmd" не будет тем, что вы захотите сделать. Даже такие вещи, как подстановки команд внутри строки, будут обработаны, поэтому следует действительно убедиться, что входные данные для eval безопасны, прежде чем использовать его.

Часто можно избежать eval и даже случайно не смешать код и данные неправильным образом.

163
27.01.2020, 19:30
создание нового дочернего процесса, выполнение аргументов и возврат статуса выхода.

Что? Весь смысл eval в том, что он никоим образом не создает дочерний процесс. Если я выполню

eval "cd /tmp"

eval в оболочке, то после этого текущая оболочка сменит каталог. Также exec не создает новый дочерний процесс, вместо этого он меняет текущий исполняемый файл (а именно оболочку) на данный; идентификатор процесса (и открытые файлы и прочее) остаются прежними. В отличие от eval, exec не возвращается в вызывающую оболочку, если только сам exec не потерпит неудачу из-за невозможности найти или загрузить исполняемый файл или из-за проблем с расширением аргументов.

eval в основном интерпретирует свой аргумент(ы) как строку после конкатенации, то есть он будет делать дополнительный уровень расширения подстановочных знаков и разделения аргументов. exec не делает ничего подобного.

5
27.01.2020, 19:30

exec не создает новый процесс. Он заменяет текущий процесс новой командой. Если вы сделали это в командной строке, то это фактически завершит вашу сессию shell (и, возможно, выведет вас из системы или закроет окно терминала!)

например

ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh% 

Здесь я нахожусь в ksh (моя обычная оболочка). Я запускаю bash, а затем внутри bash я exec /bin/echo. Мы видим, что после этого я был сброшен обратно в ksh, потому что процесс bash был заменен на /bin/echo.

30
27.01.2020, 19:30

TL;DR

exec используется для замены текущего процесса оболочки новым и обработки перенаправления потока/дескрипторов файлов, если команда не указана. eval используется для оценки строк как команд. Оба могут использоваться для создания и выполнения команды с аргументами, известными во время выполнения, но exec заменяет процесс текущей оболочки в дополнение к выполнению команд.

exec buil-in

Синтаксис:

exec [-cl] [-a name] [command [arguments]]

Согласно мануалу, если указана команда, эта встроенная

...заменяет оболочку. Новый процесс не создается. Аргументы становятся аргументами команды.

Другими словами, если вы запускали bash с PID 1234 и запускали exec top -u root в этой оболочке, top команда будет иметь PID 1234 и заменит ваш процесс оболочки.

Где это полезно? В чем-то, известном как сценарии-оболочки. Такие сценарии создают наборы аргументов или принимают определенные решения о том, какие переменные передавать в среду, а затем используют exec для замены себя любой указанной командой и, конечно же, предоставляют те же аргументы, что и сценарий-оболочка. построенный по пути.

В руководстве также говорится следующее:

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

Это позволяет нам перенаправлять что-либо из выходных потоков текущей оболочки в файл. Это может быть полезно для регистрации или фильтрации, когда вы не хотите видеть stdout команд, а только stderr. Например, вот так:

bash-4.3$ exec 3>&1
bash-4.3$ exec > test_redirect.txt
bash-4.3$ date
bash-4.3$ echo "HELLO WORLD"
bash-4.3$ exec >&3
bash-4.3$ cat test_redirect.txt 
2017年 05月 20日 星期六 05:01:51 MDT
HELLO WORLD

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

На уровне исходного кода по крайней мере для bash версии 4.3 встроенный exec определен в builtins/exec.def. Он анализирует полученные команды и, если они есть, передает их функции shell_execve(), определенной в файле execute_cmd.c.

Короче говоря, существует семейство команд exec в языке программирования C, и shell_execve() по сути представляет собой функцию-оболочку для execve:

/* Call execve (), handling interpreting shell scripts, and handling
   exec failures. */
int
shell_execve (command, args, env)
     char *command;
     char **args, **env;
{

eval встроенный

В руководстве по bash 4.3 указано (выделение добавлено мной):

Аргументы считываются и объединяются в одну команду. Затем эта команда считывается и выполняется оболочкой, и ее статус выхода возвращается как значение eval.

Обратите внимание, что замены процесса не происходит.В отличие от exec, где целью является имитация функциональности execve(), встроенный eval служит только для «оценки» аргументов, как если бы пользователь набрал их в командной строке. Таким образом, создаются новые процессы.

Где это может быть полезно? Как указал Жиль в этом ответе , «... eval используется не очень часто. В некоторых оболочках наиболее распространенным использованием является получение значения переменной, имя которой неизвестно до времени выполнения». Лично я использовал его в паре скриптов на Ubuntu, где было необходимо выполнить/оценить команду на основе конкретной рабочей области, которую в данный момент использовал пользователь.

На уровне исходного кода он определен в builtins/eval.def и передает проанализированную входную строку в функцию evalstring().

Помимо прочего, eval может назначать переменные, которые остаются в текущей среде выполнения оболочки, в то время как exec не может:

$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found
17
27.01.2020, 19:30

Оценка

Эти работы:

$ echo hi
hi

$ eval "echo hi"
hi

$ exec echo hi
hi

Однако это не:

$ exec "echo hi"
bash: exec: echo hi: not found

$ "echo hi"
bash: echo hi: command not found

Замена образа процесса

Этот пример демонстрирует, как execзаменяет образ своего вызывающего процесса:

# Get PID of current shell
sh$ echo $$
1234

# Enter a subshell with PID 5678
sh$ sh

# Check PID of subshell
sh-subshell$ echo $$
5678

# Run exec
sh-subshell$ exec echo $$
5678

# We are back in our original shell!
sh$ echo $$
1234

Обратите внимание, что exec echo $$выполняется с PID подоболочки! Кроме того, после того, как он был завершен, мы вернулись в нашу исходную sh$оболочку.

С другой стороны, evalне не заменяет образ процесса. Скорее, он запускает данную команду, как обычно в самой оболочке. (Конечно, если вы запускаете команду, которая требует запуска процесса... она делает именно это!)

sh$ echo $$
1234

sh$ sh

sh-subshell$ echo $$
5678

sh-subshell$ eval echo $$
5678

# We are still in the subshell!
sh-subshell$ echo $$
5678
0
27.01.2020, 19:30

Теги

Похожие вопросы