Я пишу некоторые функции оболочки, которые позволяют печатать трассировку стека при возникновении ошибок. Для этого я использую массив BASH_LINENO
, который содержит номер строки для каждого кадра. Затем я извлекаю строку из файла, используя массив BASH_SOURCE
и подпроцесс, например line = "$(tail -n+$lineno "$file" | head -n1)"
.
В любом случае, это работает хорошо, за исключением случаев, когда ошибка возникает внутри eval
. Проблема в том, что номер строки соответствует строке после, выражение, данное eval
, было расширено. Поэтому, когда я получаю строку с головой и хвостом, очевидно, что это не та строка, или это вообще не строка (lineno превосходит количество строк в файле).
Поэтому мне интересно, как я мог получить фактическую расширенную строку. Я просмотрел переменные, предоставленные Bash, но в этом случае, похоже, ничего не помогает.
Пример, script1.sh:
#!/usr/bin/env bash
eval "$(./script2.sh)"
script2.ш:
#!/usr/bin/env bash
echo
echo
echo
echo false
Когда я нажимаю строку false
при выполнении script1.sh
, номер строки, который я получаю, равен 4, а источник файла, который я получаю, — script1.sh
, так что это неправильно.
Когда строка отсутствует в файле, я мог бы обнаружить это и вместо этого напечатать первую предыдущую строку eval, но это очень сложно, и я уверен, что есть несколько разных случаев, которые нужно обработать. А если строка внутри файла, то я даже не могу знать, правильная она или нет.
eval
это ад :'(
В идеале BASH_COMMAND
также должен быть массивом, и я мог бы извлекать из него команды вместо чтения файлов.
Еще одна идея, которую я только что придумал, заключалась в том, чтобы заставить пользователя передать результат выражения в команду, которая будет сжимать его в одну строку. Любые идеи, как или программы для этого? Простое соединение на ";" кажется наивным (опять же, много крайних случаев).
P.S.: извините за название, мне трудно дать осмысленное название этому :/
В конце концов я нашел обходной путь: переопределив команду 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)"