Получить содержимое расширенного выражения, переданного eval через Bash internals

#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)"