Яка різниця між eval та exec?


81

evalі execвбудовані в команди bash (1), які виконують команди.

Я також бачу, execщо є кілька варіантів, але хіба це єдина різниця? Що відбувається з їх контекстом?



Відповіді:


124

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оболонкою і 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і уникнути навіть випадкового змішування коду та даних неправильним способом.


Це чудова відповідь, @ilkkachu. Дякую!
Вілліан Пайсао

Більш докладно про використання Eval можна знайти тут: stackoverflow.com/a/46944004/2079103
Clearlight

1
@clearlight, ну, це нагадує мені , щоб додати звичайний відмова не використовується evalв першу чергу для цієї відповіді теж. Такі речі, як непряма модифікація змінних, можна зробити в багатьох оболонках через declare/ typeset/ namerefі розширення, як-от ${!var}, тому я використовував би їх замість, evalякщо тільки мені не довелося цього уникати.
ilkkachu

27

execне створює нового процесу. Він замінює поточний процес новою командою. Якщо ви зробили це в командному рядку, то він фактично закінчить сеанс оболонки (і, можливо, вийде з системи або закриє вікно терміналу!)

напр

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

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


ммм на обличчі відкинуто назад на ksh b / c процес замінив ехо, чи не має такого великого сенсу?

14

TL; DR

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

exec вбудований

Синтаксис:

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

Базовий 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

@ ctrl-alt-delor Я відредагував цю частину, дякуючи, хоча, мабуть, вона породжує новий процес, PID просто залишається таким же, як у поточної оболонки. В майбутньому розгляньте можливість редагування відповідей, а не залишати коментар та коментар, особливо там, де йдеться про таке незначне, як 7-словесне словосполучення; для редагування та виправлення відповіді набагато менше часу, і це набагато корисніше.
Сергій Колодяжний

5
створивши новий дочірній процес, запустіть аргументи та поверніть статус виходу.

Ага, що? Вся справа в evalтому, що це жодним чином не створює дитячого процесу. Якщо я це роблю

eval "cd /tmp"

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

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


1
Оригінальне запитання: "eval і exec - це вбудовані команди Bash (1) і виконує команди, створюючи новий дочірній процес, запускаючи аргументи і повертаючи статус виходу"; Я відредагував помилкову презумпцію.
Чарльз Стюарт

-1

Оцінка

Ці роботи:

$ 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

Я опублікував цю відповідь, тому що інші приклади насправді не ілюстрували це досить зрозумілим чином для слабких розумів, таких як я.
Mateen Ulhaq

Поганий вибір слів: Заміна процесу - це існуюча концепція, яка, здається, не пов'язана з тим, що ви тут ілюструєте.
муру

@muru Чи краще "заміна процесу"? Я не впевнений, яка правильна термінологія. Здається, що ця сторінка називає її "заміною зображення процесу".
Mateen Ulhaq

Хм, даун. (Але коли я чую «заміна способу процесу», я думаю: exec)
Муру
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.