Как использовать eval в скриптах Linux Bash
Опубликовано: 2022-08-22
Из всех команд Bash у бедного старого eval
, вероятно, худшая репутация. Оправдано или просто плохая пресса? Мы обсудим использование и опасности этой наименее любимой команды Linux.
Нам нужно поговорить об eval
При неосторожном использовании eval
может привести к непредсказуемому поведению и даже к ненадежности системы. Судя по его звукам, мы, вероятно, не должны его использовать, верно? Ну не совсем.
Нечто подобное можно сказать и об автомобилях. В неумелых руках это смертельное оружие. Люди используют их в таранных рейдах и в качестве транспортных средств для побега. Должны ли мы все перестать пользоваться автомобилями? Нет, конечно нет. Но они должны использоваться правильно и людьми, которые умеют ими управлять.
Обычное прилагательное, применяемое к eval
, — «зло». Но все зависит от того, как он используется. Команда eval
сопоставляет значения одной или нескольких переменных. Он создает командную строку. Затем он выполняет эту команду. Это делает его полезным, когда вам нужно справляться с ситуациями, когда содержимое команды извлекается динамически во время выполнения вашего скрипта.
Проблемы возникают, когда сценарий написан так, чтобы использовать eval
для строки, полученной откуда-то вне сценария. Он может быть введен пользователем, отправлен через API, помечен в HTTPS-запросе или где-либо еще вне сценария.
Если строка, над которой будет работать eval
, не была получена локально и программно, существует риск того, что строка может содержать встроенные вредоносные инструкции или другие вводные данные неправильного формата. Очевидно, вы не хотите, чтобы eval
выполнял вредоносные команды. Поэтому, чтобы быть в безопасности, не используйте eval
со строками, сгенерированными извне, или пользовательским вводом.
Первые шаги с eval
Команда eval
— это встроенная команда оболочки Bash. Если присутствует Bash, будет присутствовать и eval
.
eval
объединяет свои параметры в одну строку. Он будет использовать один пробел для разделения конкатенированных элементов. Он оценивает аргументы, а затем передает всю строку в оболочку для выполнения.
Давайте создадим переменную с именем wordcount
.
количество слов = «wc -w raw-notes.md»
Строковая переменная содержит команду для подсчета слов в файле с именем «raw-notes.md».
Мы можем использовать eval
для выполнения этой команды, передав ей значение переменной.
eval " $wordcount "
Команда выполняется в текущей оболочке, а не в подоболочке. Мы можем легко показать это. У нас есть короткий текстовый файл с именем «variables.txt». Он содержит эти две строки.
первый = как второй = Компьютерщик
Мы будем использовать cat
для отправки этих строк в окно терминала. Затем мы будем использовать eval
для оценки команды cat
, чтобы выполнить инструкции внутри текстового файла. Это установит переменные для нас.
кошачьи переменные.txt eval "$(переменные кота.txt)" эхо $первая $секунда
Используя echo
для вывода значений переменных, мы видим, что команда eval
выполняется в текущей оболочке, а не в подоболочке.
Процесс в подоболочке не может изменить среду оболочки родителя. Поскольку eval работает в текущей оболочке, переменные, заданные eval
, можно использовать из оболочки, которая запустила команду eval
.
Обратите внимание, что если вы используете eval
в сценарии, оболочка, которая будет изменена eval
, является подоболочкой, в которой выполняется сценарий, а не оболочкой, которая его запустила.
СВЯЗАННЫЕ С: Как использовать команды Linux cat и tac
Использование переменных в командной строке
Мы можем включать другие переменные в командные строки. Мы установим две переменные для хранения целых чисел.
число1=10 число2=7
Мы создадим переменную для хранения команды expr
, которая будет возвращать сумму двух чисел. Это означает, что нам нужно получить доступ к значениям двух целочисленных переменных в команде. Обратите внимание на обратные кавычки вокруг оператора expr
.
add="`выражение $num1 + $num2`"
Мы создадим еще одну команду, чтобы показать нам результат оператора expr
.
показать = "эхо"
Обратите внимание, что нам не нужно включать пробел ни в конец строки echo
, ни в начало строки expr
. eval
позаботится об этом.
И для выполнения всей команды мы используем:
eval $ показать $ добавить
Значения переменных внутри строки expr
подставляются в строку с помощью eval
до того, как она будет передана оболочке для выполнения.
СВЯЗАННЫЕ С: Как работать с переменными в Bash
Доступ к переменным внутри переменных
Вы можете присвоить значение переменной, а затем присвоить имя этой переменной другой переменной. Используя eval
, вы можете получить доступ к значению , хранящемуся в первой переменной, по ее имени, которое является значением , хранящимся во второй переменной. Пример поможет вам разобраться в этом.
Скопируйте этот сценарий в редактор и сохраните его как файл с именем «assign.sh».
#!/бин/баш title="Как гик" веб-страница=название команда = "эхо" eval $команда \${$веб-страница}
Нам нужно сделать его исполняемым с помощью команды chmod
.
chmod +x assign.sh

Вам нужно будет сделать это для всех скриптов, которые вы скопируете из этой статьи. Просто используйте соответствующее имя сценария в каждом случае.
Когда мы запускаем наш скрипт, мы видим текст из переменной title
, хотя команда eval
использует переменную webpage
.
./назначить.ш
Экранированный знак доллара « $
» и фигурные скобки « {}
» заставляют eval просматривать значение, хранящееся внутри переменной, имя которой хранится в переменной webpage
-страницы.
Использование динамически создаваемых переменных
Мы можем использовать eval
для динамического создания переменных. Этот скрипт называется «loop.sh».
#!/бин/баш всего=0 label="Цикл завершен. Итого:" для n в {1..10} делать оценка x$n=$n эхо "Цикл" $x$n ((всего+=$x$n)) Выполнено эхо $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10 эхо $ метка $ всего
Он создает переменную с именем total
, которая содержит сумму значений созданных нами переменных. Затем он создает строковую переменную с именем label
. Это простая строка текста.
Мы зациклимся 10 раз и создадим 10 переменных с именами от x1
до x10
. Оператор eval
в теле цикла предоставляет «x» и принимает значение счетчика цикла $n
для создания имени переменной. В то же время он устанавливает новую переменную в значение счетчика цикла $n
.
Он выводит новую переменную в окно терминала, а затем увеличивает total
переменную на значение новой переменной.
Вне цикла 10 новых переменных печатаются еще раз, все в одной строке. Обратите внимание, что мы также можем ссылаться на переменные по их реальным именам, не используя вычисляемую или производную версию их имен.
Наконец, мы печатаем значение total
переменной.
./loop.sh
СВЯЗАННЫЕ: Учебник: Циклы Bash: for, while и until
Использование eval с массивами
Представьте себе сценарий, в котором у вас есть скрипт, который долго работает и выполняет некоторую обработку для вас. Он записывает в файл журнала с именем, созданным из метки времени. Иногда он запускает новый файл журнала. Когда сценарий завершится, если ошибок не было, он удалит созданные им файлы журналов.
Вы не хотите, чтобы он просто rm *.log
, вы только хотите, чтобы он удалял файлы журнала, которые он создал. Этот скрипт имитирует эту функциональность. Это «clear-logs.sh».
#!/бин/баш объявить -a лог-файлы количество файлов = 0 rm_string="эхо" функция create_logfile() { ((++число файлов)) имя_файла=$(дата +"%Y-%m-%d_%H-%M-%S").log logfiles[$filecount]=$имя файла echo $filecount "Создано" ${logfiles[$filecount]} } # тело скрипта. Здесь выполняется некоторая обработка, которая # периодически генерирует файл журнала. Мы смоделируем это create_logfile спать 3 create_logfile спать 3 create_logfile спать 3 create_logfile # есть ли файлы для удаления? for ((файл=1; файл<=$filecount; файл++)) делать # удалить лог-файл eval $rm_string ${logfiles[$file]} "удален..." лог-файлы[$файл]="" Выполнено
Сценарий объявляет массив с именем logfiles
. Здесь будут храниться имена файлов журналов, созданных сценарием. Он объявляет переменную с именем filecount
. Здесь будет храниться количество созданных файлов журнала.
Он также объявляет строку с именем rm_string
. В реальном сценарии это будет содержать команду rm
, но мы используем echo
, чтобы продемонстрировать принцип неразрушающим образом.
Функция create_logfile()
определяет имя каждого файла журнала и место, где он будет открыт. Мы только создаем имя файла и делаем вид, что оно было создано в файловой системе.
Функция увеличивает переменную filecount
. Его начальное значение равно нулю, поэтому первое имя файла, которое мы создаем, сохраняется в первой позиции в массиве. Это сделано специально, как об этом далее.
Имя файла создается с помощью команды date
и расширения «.log». Имя хранится в массиве в позиции, указанной в filecount
. Имя печатается в окне терминала. В реальном сценарии вы также должны создать реальный файл.
Тело скрипта моделируется с помощью команды sleep
. Он создает первый файл журнала, ждет три секунды, а затем создает еще один. Он создает четыре файла журнала, разнесенные таким образом, что временные метки в именах файлов различаются.
Наконец, есть цикл, который удаляет файлы журнала. Файл счетчика циклов установлен в единицу. Он считает до значения filecount
, которое содержит количество созданных файлов.
Если filecount
по-прежнему установлено равным нулю (поскольку файлы журналов не создавались), тело цикла никогда не будет выполнено, поскольку единица не меньше или равна нулю. Вот почему переменная filecount
была установлена в ноль, когда она была объявлена, и почему она была увеличена до того, как был создан первый файл.
Внутри цикла мы используем eval
с нашей недеструктивной rm_string
и именем файла, который извлекается из массива. Затем мы устанавливаем элемент массива в пустую строку.
Это то, что мы видим, когда запускаем скрипт.
./clear-logs.sh
Не все так плохо
Много оклеветанного eval
определенно имеет свое применение. Как и большинство инструментов, при неосторожном использовании он опасен, причем по многим причинам.
Если вы убедитесь, что строки, с которыми он работает, создаются внутри, а не перехватываются людьми, API или такими вещами, как HTTPS-запросы, вы избежите основных ловушек.
СВЯЗАННЫЕ С: Как отображать дату и время в терминале Linux (и использовать их в сценариях Bash)