Замена переменной Bash в функции

#regex #bash

#регулярное выражение #bash

Вопрос:

У меня есть функция read_command, определенная как:

 function read_command {
    local __newline __lines __input __newstr __tmp i;
    exec 3< "$*";
    __newline=$'n';
    __lines=();
    while IFS= read <amp;3 -r __line amp;amp; [ "$__line" != '####' ]; do
        echo "$__line";
        __lines =("$__line");
    done
    while IFS= read <amp;3 -r __line amp;amp; [ "$__line" != '####' ]; do
        read -e -p "${__line#*:}$PS2" __input;
        local ${__line%%:*}="$__input";
    done
    __command="";
    for i in "${__lines[@]}"; do
        __tmp=$(echo "${i}");
        __command="${__command} ${__newline} ${__tmp}";
    done
    echo -e "$__command";
}
  

В текущем каталоге есть файл с именем «test» со следующим
содержимым:

 greet ${a:-"Bob"} 
greet ${b:-"Jim"}
####
a: name of friend a
b: name of friend b
####
  

В терминале выполняется команда

 read_command test
  

Без ввода я ожидаю, что вывод последнего оператора будет:

 greet Bob
greet Jim
  

Но то, что я получаю, это:

 greet ${a:-"Bob"}  
greet ${b:-"Jim"}
  

Что здесь не так?

Редактировать: как предложил Дэвид, добавление eval работает в некоторых случаях, кроме следующего.

 j=1;i="export DISPLAY=:0 amp;amp; Xephyr :${j}amp;";k=$(eval echo "$i");
echo $k
  

экспорт ОТОБРАЖЕНИЯ =:0

Я ожидаю, что k будет «export DISPLAY=:0 amp;amp; Xephyr :1 amp;», что здесь не так? Редактировать: я попробовал со следующим

 k=$(eval "echo "$i"")
  

Это ссылка на скрипт, над которым я работаю.
https://gist.github.com/QiangF/565102ba3b6123942b9bf6b897c05f87

Комментарии:

1. Точки с запятой в конце строки являются излишними; в сценарии оболочки в качестве разделителя команд достаточно новой строки или точки с запятой.

2. Я просто следую примеру, в Интернете слишком много плохих примеров.

Ответ №1:

Во время первого while цикла, в echo "$__line" , у вас есть __line='greet ${a:-"Bob"}' . Когда вы попытаетесь это напечатать, Bash не будет расширяться ${a:-"Bob"} Bob . (Даже если вы удалите кавычки $__line , этого не произойдет.) Чтобы получить этот эффект, вам нужно добавить eval , как, например, eval echo "$__line" . К сожалению eval , поставляется с банкой червей, вам нужно начать беспокоиться о взаимодействии между уровнями цитирования и тому подобным.

Комментарии:

1. Как предотвратить выполнение команды eval echo $ __line в __line, см. Редактирование в вопросе.

2. Это очень похоже на проблему XY . Какую проблему вы на самом деле пытаетесь решить с помощью этого кода? Это также смутно похоже на mywiki. wooledge.org/BashFAQ/050

3. Альтернатива обману. sh должен использовать замену шаблона, но это менее удобно. ( github.com/tests-always-included/mo )

4. eval использует первичный уровень цитирования, что означает, что: eval $v и eval "$v" сделайте то же самое для всего содержимого $v . Чтобы управлять тем, что bash происходит после расширения eval аргументов, вам нужно использовать кавычки второго уровня, то есть кавычки, которые не будут использоваться на первом уровне. Итак, попробуйте eval echo "$line" . Все быстро становится неприятным, если $line также содержит кавычки, но, похоже, в вашем случае это не так, так что все должно быть в порядке.