Поиск и замена переменных в файле с использованием bash/sed
Я пытаюсь написать скрипт bash (script.sh) для поиска и замены некоторых переменных в файле input.sh. Но мне нужно изменить только те переменные, которые присутствуют в файле variable_list и оставить другие как есть.
variable_list
${user}
${dbname}
input.sh
username=${user}
password=${password}
dbname=${dbname}
Ожидаемый выходной файл
username=oracle
password=${password} > This line won't be changed as this variable(${password}) is not in variable_list file
dbname=oracle
Ниже приведен скрипт, который я пытаюсь использовать, но я не могу найти правильное выражение sed
script.sh
export user=oracle
export password=oracle123
export dbname=oracle
variable='variable_list'
while read line ;
do
if [[ -n $line ]]
then
sed -i 's/$line/$line/g' input.sh > output.sh
fi
done < "$variable"
4 ответа
Это может сработать:
#!/bin/bash
export user=oracle
export password=oracle123
export dbname=oracle
variable='variable_list'
while read line ;
do
if [[ -n $line ]]
then
exp=$(sed -e 's/\$/\\&/g' <<< "$line")
var=$(sed -e 's/\${\([^}]\+\)}/\1/' <<< "$line")
sed -i "s/$exp/${!var}/g" input.sh
fi
done < "$variable"
Первый sed
выражение избегает $, который является метасимволом регулярных выражений. Второй извлекает только имя переменной, затем мы используем косвенное обращение, чтобы получить значение в нашей текущей оболочке и использовать его в sed
выражение.
редактировать
Вместо того, чтобы переписывать файл так много раз, вероятно, более эффективно сделать это следующим образом, составив список аргументов для sed
:
#!/bin/bash
export user=oracle
export password=oracle123
export dbname=oracle
while read var
do
exp=$(sed -e 's/\$/\\&/g' <<< "$var")
var=$(sed -e 's/\${\([^}]\+\)}/\1/' <<< "$var")
args+=("-e s/$exp/${!var}/g")
done < "variable_list"
sed "${args[@]}" input.sh > output.sh
user=oracle
password=oracle123
dbname=oracle
variable_list=( '${user}' '${dbname}' )
while IFS="=$IFS" read variable value; do
for subst_var in "${variable_list[@]}"; do
if [[ $subst_var = $value ]]; then
eval "value=$subst_var"
break
fi
done
printf "%s=%s\n" "$variable" "$value"
done < input.sh > output.sh
Вот скрипт.sh, который работает:
#!/bin/bash
user=oracle
password=oracle123
dbname=oracle
variable='variable_list'
text=$(cat input.sh)
while read line
do
value=$(eval echo $line)
text=$(sed "s/$line/$value/g" <<< "$text")
done < "$variable"
echo "$text" > output.sh
Обратите внимание, что ваша оригинальная версия содержит одинарные кавычки вокруг строки sed, которая не вставляет значение $line
, Пытается найти буквальное line
после конца строки $
(который никогда ничего не найдет).
Так как вы ищете значение переменной в $line
Вам нужно сделать eval, чтобы получить это.
Кроме того, поскольку есть несколько переменных, над которыми вы зацикливаетесь, промежуточный text
переменная хранит результат в цикле.
export
Ключевое слово также не требуется в этом сценарии, если только оно не используется в некотором подпроцессе, который не показан.
Решение TXR. Построить фильтр динамически. Фильтр реализован внутри как трехуровневая структура данных, которая дает нам lex
-подобный конечный автомат, который совпадает со всем словарем одновременно при сканировании ввода. Для простоты мы включаем ${
а также }
как часть имени переменной.
@(bind vars (("${user}" "oracle")
("${dbname}" "oracle")
("${password}" "letme1n")))
@(next "variable_list")
@(collect)
@entries
@(end)
@(deffilter subst . @(mapcar (op list @1 (second [find vars @1 equal first]))
entries))
@(next "input.sh")
@(collect)
@line
@ (output :filter subst)
@line
@ (end)
@(end)
Бежать:
$ txr subst.txr
username=oracle
password=${password}
dbname=oracle
input.sh:
(как дано)
username=${user}
password=${password}
dbname=${dbname}
variable_list:
(как дано)
${user}
${dbname}