#bash
#bash
Вопрос:
Я пишу некоторые функции оболочки, которые позволяют печатать трассировки стека при возникновении ошибок. Для этого я использую BASH_LINENO
массив, который содержит номер строки для каждого кадра. Затем я извлекаю строку из файла, используя BASH_SOURCE
array и подобный подпроцесс line="$(tail -n $lineno "$file" | head -n1)"
.
В любом случае, это работает хорошо, за исключением случаев, когда ошибка возникает внутри eval
. Проблема в том, что номер строки соответствует строке после того, как выражение, заданное для eval
, было расширено. Поэтому, когда я извлекаю строку с заголовком и хвостом, очевидно, что теперь она неправильная или это вообще не строка (lineno превосходит количество строк в файле).
Итак, мне интересно, как я мог бы получить фактическую расширенную строку. Я просмотрел переменные, предоставленные Bash, но, похоже, ни одна из них не помогает в этом случае.
Пример, script1.sh:
#!/usr/bin/env bash
eval "$(./script2.sh)"
script2.sh:
#!/usr/bin/env bash
echo
echo
echo
echo false
Когда я нажимаю на false
строку при выполнении script1.sh
, номер строки, который я получаю, равен 4, а источник файла, который я получаю script1.sh
, равен, так что это неправильно.
Когда строка отсутствует в файле, я мог бы ее обнаружить и вместо этого напечатать первую предыдущую строку eval, но это очень сложно, и я уверен, что есть несколько разных случаев для обработки. И если строка находится внутри файла, то я даже не могу знать, является ли она правильной или нет.
eval
это ад:'(
В идеале, BASH_COMMAND
это также был бы массив, и я мог бы извлекать из него команды вместо чтения файлов.
Еще одна идея, которая у меня только что появилась, — заставить пользователя передать результат выражения в команду, которая будет сжимать его в одной строке. Есть идеи, как или программы для этого? Простое объединение в «;» кажется наивным (опять же, много крайних случаев).
PS: извините за заголовок, мне трудно дать этому значимое название :/
Ответ №1:
В конце концов я нашел обходной путь: переопределив eval
команду своей собственной функцией, я смог изменить способ печати трассировки стека для ошибок, возникающих в eval
операторах.
eval() {
# pre eval logic
command eval "$@"
# post eval logic
}
В любом случае, пожалуйста, не используйте eval
, или если вы используете, используйте только аргументы одной строки:
# GOOD: "easy" to deal with
for i in ...; do
eval "$(some command)"
done
# BAD: this will mess up your line numbers
eval "$(for i in ...; do
some command $i
done)"