2014-11-27 3 views
3

Я пишу простой инструмент для отладки bash, log_next_line. Предполагаемое поведение - когда переменная log определена, она читает следующую строку сценария, разворачивает на нем переменные, записывает его в файл, указанный $log, выполняет команду и выводит ее вывод в тот же журнал.Как выполнить переменное расширение строковой переменной в bash?

У меня проблема с переменным расширением. Предположим, что у меня есть строка кода bash в переменной $line. Как расширить строки в нем без выполнения каких-либо внешних программ и без выполнения труб (что опасно)?

Простой line=$("echo $line") не работает вообще. Когда я пытаюсь line=$(bash -c "echo $line") Мне повезло больше, но тогда мне нужно экспортировать ВСЕ переменные bash в порожденный bash (который я понятия не имею, как это сделать). И вызов внешней программы для нее кажется излишним; кроме того, bash будет выполнять трубы и внешние программы, если, например, line="echo $(rm -r /)".

Есть ли способ сделать расширение переменной, которое не будет включать в себя запись парсера Bash с нуля ;-)? Мне нужно, чтобы он работал под Linux (Ubuntu> = 14.04, если быть точным).

Для полной картины я включаю прототип функции:

function log_next_line() 
{ 
    if [ -n "$log" ]; then 
     file=${BASH_SOURCE[1]##*/} #Takes path to the file, from where the function is called 
     linenr=$((${BASH_LINENO[0]} + 1)) #Line number 
     line=`sed "${linenr}q;d" $file` #Reads this line number from the source file 
     line=$("echo $line") #DOESN'T WORK. I want it to do a variable expansion 
     echo "\$ $line" >>$log #Writes the variable-expanded line to the log 
     $line >>$log 2>>$log #Executes the line and 
     exitstatus=$? 
     if [ "$exitstatus" -ne "0" ]; then 
      echo "## Exit status: $exitstatus" >>$log 
     fi 
     echo >>$log 
     BASH_LINENO[0]=$((BASH_LINENO[0] + 1)) #Skips executing of the next line, since we have already executed it (and captured its output) 
     if [ "$exitstatus" -ne "0" ]; then 
      exit $exitstatus 
     fi 
    fi 
} 

и простой скрипт, который может быть использован в качестве тестового примера

#!/bin/bash 

log=mylog.log 

. ./log_next_line.sh 

rm mylog.log 

a=4 
b=example 
x=a 

log_next_line 
echo $x 
log_next_line 
echo ${!b} 
log_next_line 
touch ${!b}.txt > /dev/null 
log_next_line 
touch ${!x}.txt 
log_next_line 
if [ ((${#a} - 6 )) -gt 10 ]; then echo "Too long string";fi 
log_next_line 
echo "\$a and \$x"|tee file.txt 

модульных тестов:

if x=a, a="example" then Хочу следующие расширения:

  1. echo $x должно быть echo a.
  2. echo ${!b} должен быть echo example
  3. touch ${!b}.txt>/dev/null должен быть touch example.txt>/dev/null
  4. if [ ((${#a} - 6 )) -gt 10 ]; then echo "Too long string";fi должен быть if [ 1 -gt 10 ]; then echo "Too long string";fi
  5. echo "\$a and \$x"|tee file.txt должен быть echo "\$a and \$x"|tee file.txt"

Этот вопрос является обобщением https://unix.stackexchange.com/questions/131150/bash-is-it-safe-to-eval-bash-command. Ответы, приведенные здесь, не проходят все мои тестовые примеры.

+0

Я предполагаю, что вы знакомы с опцией '-x' shell? Он делает именно то, что вы пытаетесь реализовать, но, как правило, без необходимости изменять источники. – user3159253

+1

Почему вы делаете 'line = sed '$ {linenr} q; d" $ file'? Это похоже на кошмар расширения. Почему бы не 'line = $ (sed -n $ {linenr} p" $ file ")'.Это приведет к '$ linenr' из' $ file', не вставляя конец строки в двойные кавычки, который, даже если он работает, является рельсом. –

ответ

1

функция может быть реализована как простой set -x, чтобы включить трассировку в заданной точке сценария и set +x, чтобы отключить ее.

Если вы определенно хотите реализовать его как флаг одной строки, я бы посмотрел на this question и настроил флаг -x через ловушку DEBUG.

Смежные вопросы