Поиск и замена переменных в файле с использованием 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}
Другие вопросы по тегам