Яка різниця між “eval” та “source / dev / stdin”?


17

Між наведеними нижче альтернативами ...

  1. з eval.

    comd="ls"
    eval "$comd"
  2. з source /dev/stdin

    printf "ls" | source /dev/stdin
  3. з source /dev/stdinі ( )або{ }

    ( printf "ls" ) | source /dev/stdin
    { printf "ls"; } | source /dev/stdin

    (Коли ми біжимо printfв { }, є якісь - які переваги, крім не використовуючи подоболочкі?)

    • У чому різниця між ними?

    • Що є кращим?

    • Який найкращий спосіб запускати команди? ()або {}?


1
Я б не рекомендував жоден підхід. Що ви насправді намагаєтесь зробити , що, на вашу думку, потрібно виконати довільний код, поданий користувачем?
чепнер

2
Я також хоч виконував довільне введення користувача (як це є), поки я не прочитав питання. Але, можливо, ви прогнозуєте, що вони будуть.
ctrl-alt-delor

Відповіді:


17
  • У чому різниця між способами?

від bash manpage:

eval [arg ...]
              The  args  are read and concatenated together into a single com
              mand.  This command is then read and executed by the shell,  and
              its  exit status is returned as the value of eval.  If there are
              no args, or only null arguments, eval returns 0.

source filename [arguments]
              Read and execute commands from filename  in  the  current  shell
              environment  and return the exit status of the last command exe
              cuted from filename.  If filename does not contain a slash, file
              names  in  PATH  are used to find the directory containing file
              name.  The file searched for in PATH  need  not  be  executable.
              When  bash  is  not  in  posix  mode,  the  current directory is
              searched if no file is found in PATH.  If the sourcepath  option
              to  the  shopt  builtin  command  is turned off, the PATH is not
              searched.  If any arguments are supplied, they become the  posi
              tional  parameters  when  filename  is  executed.  Otherwise the
              positional parameters are unchanged.  The return status  is  the
              status  of  the  last  command exited within the script (0 if no
              commands are executed), and false if filename is  not  found  or
              cannot be read.

Відмінності між двома способами немає.

Є лише одна примітка: evalоб'єднав усі свої аргументи, які потім запускаються як одна команда. sourceчитає вміст файлу та виконує їх. evalможе будувати команди лише зі своїх аргументів, ні stdin. Тож не можна так робити:

printf "ls" | eval
  • Що є більш переважним?

Ваш приклад дає той самий результат, але призначення evalта sourceінше. sourceзазвичай використовується для надання бібліотеки для інших сценаріїв, тоді evalяк використовується лише для оцінки команд. Вам слід уникати використання, evalякщо це можливо, тому що немає гарантії, що рядок, що ухиляється, чистий; ми повинні зробити кілька перевірок санітарності, використовуючи subshellзамість цього.

  • Якщо ми виконаємо деякі команди в () або {}, що є більш кращим?

Коли ви запускаєте команди послідовностей всередині фігурної дужки { }, всі команди виконуються в поточній оболонці , а не в підзаголовок (що є випадком, якщо ви запускаєтеся в дужки (див. Посилання bash )).

Використання subshell ( )використовує більше ресурсів, але ваше поточне середовище не впливає. Використання { }виконує всі команди в поточній оболонці, тому впливає ваше оточення. Залежно від вашого призначення, ви можете вибрати одну з них.


2
Я думаю, ви неправильно зрозуміли питання. Звичайно, ви не можете замінити evalїх source. Я здогадуюсь, питання: eval "$cmd"еквівалентно echo "$cmd" | source /dev/stdin. Моя сьогоднішня думка така: так.
Hauke ​​Laging

3

Основна відмінність полягає в тому, що в 2-й і 3-й формах використовується труба, яка змусить bash запустити команду "source" в нижній частині корпусу (якщо не встановлено lastpipe, доступний лише в bash 4.2+), що зробить його майже еквівалентним :

printf "ls" | bash

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

printf "abc=2" | source /dev/stdin

Для запуску команд у поточній оболонці ви можете використовувати підстановку процесу:

source <(printf "abc=2")

Ви можете помістити більше команд всередині дужок, використовуючи крапку з комою, як зазвичай.

Якщо ви усунете трубу таким чином, я вважаю, що немає різниці між використанням "eval" та "source". Вам слід віддати перевагу тому, який простіше використовувати у вашому конкретному випадку:

  • якщо у вас вже є команди для запуску змінної, використовуйте "eval"
  • якщо ви маєте їх у файлі або отримуєте їх із зовнішньої команди, використовуйте "source"

0

Як доповнення до вже наданих відповідей:

sourceЕквівалентно ...

comd="ls"
eval "$comd"

... є ...

source <(printf ls)

У разі lsсуттєвої різниці немає.

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

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