Оновлення: Деякі люди кажуть, що слід, коли-небудь, використовувати eval. Я не погоджуюсь. Я думаю, що ризик виникає, коли можна передати корумпований внесок eval
. Однак є багато поширених ситуацій, коли це не є ризиком, і тому варто знати, як використовувати eval у будь-якому випадку. Ця відповідь stackoverflow пояснює ризики eval та альтернативи eval. Зрештою, користувач повинен визначити, чи / коли eval безпечний та ефективний у використанні.
Оператор bash eval
дозволяє виконувати рядки коду, обчислені чи придбані, за допомогою вашого bash script.
Мабуть, найпростішим прикладом може бути програма bash, яка відкриває інший bash-скрипт у вигляді текстового файлу, читає кожен рядок тексту та використовує eval
для їх виконання по порядку. Це, по суті, така ж поведінка, як і bash- source
оператор, для чого можна було б скористатися, якщо тільки не було необхідності здійснити якусь трансформацію (наприклад, фільтрування чи заміну) вмісту імпортованого сценарію.
Мені це рідко потрібно eval
, але мені здається корисним читати чи писати змінні, імена яких містилися в рядках, призначених іншим змінним. Наприклад, виконувати дії над наборами змінних, зберігаючи при цьому код слід невеликим і уникаючи надмірності.
eval
концептуально проста. Однак суворий синтаксис мови bash та порядок розбору інтерпретатора bash можуть бути нюансовані та зробити їх eval
виразними і важкими у використанні чи розумінні. Ось основні елементи:
Переданий аргумент eval
- це рядковий вираз, який обчислюється під час виконання. eval
виконає остаточний проаналізований результат свого аргументу як фактичний рядок коду у вашому сценарії.
Синтаксис та порядок розбору є суворими. Якщо результат не є виконуваною лінією bash-коду, в межах вашого сценарію програма вийде з ладу на eval
оператор, коли він намагається виконати сміття.
Під час тестування ви можете замінити eval
оператор echo
і подивитися, що відображається. Якщо він є законним кодом у поточному контексті, його запуск eval
буде працювати.
Наступні приклади можуть допомогти з’ясувати, як працює eval ...
Приклад 1:
eval
оператор перед "звичайним" кодом - це NOP
$ eval a=b
$ eval echo $a
b
У наведеному вище прикладі перші eval
твердження не мають мети і можуть бути усунені. eval
в першому рядку безглуздо, оскільки немає динамічного аспекту коду, тобто він вже розібрався в кінцеві рядки коду bash, таким чином, він був би ідентичним як звичайний вислів коду в скрипті bash. 2-й eval
теж є безглуздим, оскільки, хоча існує крок розбору, перетворюючи $a
його в буквальний еквівалент рядка, немає ніякого опосередкування (наприклад, відсутність посилань через значення рядка фактичного іменника bash або змінної скрипта, що міститься в bash), тому він би поводився однаково як рядок коду без eval
префікса.
Приклад 2:
Виконуйте призначення var, використовуючи імена var, передані як значення рядка.
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
Якби ви були echo $key=$val
, вихід був би:
mykey=myval
Це , будучи кінцевим результатом розбору рядків, - це те, що буде виконано eval, отже, результат констатації ехо в кінці ...
Приклад 3:
Додавання додаткового непрямого до Прикладу 2
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
Вищенаведене трохи складніше, ніж попередній приклад, більше спираючись на порядок розбору та особливостей bash. eval
Лінія буде грубо отримати розібраний внутрішньо в наступному порядку (зверніть увагу на наступні твердження псевдокод, а не справжній код, просто спроба показати , як заяву буде ламаються на кроки всередині , щоб прийти до кінцевого результату) .
eval eval \$$keyA=\$$valA # substitution of $keyA and $valA by interpreter
eval eval \$keyB=\$valB # convert '$' + name-strings to real vars by eval
eval $keyB=$valB # substitution of $keyB and $valB by interpreter
eval that=amazing # execute string literal 'that=amazing' by eval
Якщо передбачуваний порядок розбору не пояснює, що eval робить достатньо, третій приклад може описати синтаксичний розбір більш детально, щоб допомогти з’ясувати, що відбувається.
Приклад 4:
З’ясуйте, чи містять рядки, назви яких містяться у рядках, самі значення рядків.
a="User-provided"
b="Another user-provided optional value"
c=""
myvarname_a="a"
myvarname_b="b"
myvarname_c="c"
for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
eval varval=\$$varname
if [ -z "$varval" ]; then
read -p "$varname? " $varname
fi
done
У першій ітерації:
varname="myvarname_a"
Bash аналізує аргумент до eval
та eval
бачить буквально це під час виконання:
eval varval=\$$myvarname_a
Наступний псевдокод намагається проілюструвати, як bash інтерпретує вищевказаний рядок реального коду, щоб дійти до кінцевого значення, виконаного eval
. (наступні рядки описові, не точний баш-код):
1. eval varval="\$" + "$varname" # This substitution resolved in eval statement
2. .................. "$myvarname_a" # $myvarname_a previously resolved by for-loop
3. .................. "a" # ... to this value
4. eval "varval=$a" # This requires one more parsing step
5. eval varval="User-provided" # Final result of parsing (eval executes this)
Після того, як буде зроблено весь синтаксичний аналіз, результат - це те, що виконується, і його ефект очевидний, демонструючи, що немає нічого особливо загадкового в eval
собі, а складність полягає в синтаксичному аналізі його аргументу.
varval="User-provided"
Код, що залишився у наведеному вище прикладі, просто перевіряє, чи є значення, присвоєне $ varval, нульовим, і, якщо так, спонукає користувача ввести значення.
$($n)
працює$n
в передпласті. Він намагається запустити команду,1
якої не існує.