2017-02-21 84 views
0

У меня есть несколько файлов конфигурации с настраиваемыми данными внутри них. Пример (смотрите на ${ConnectorPort}):Как использовать скрипт bash для замены ключей значениями в файлах с помощью файла свойств в качестве входа

<Connector port="${ConnectorPort}" protocol="HTTP/1.1" server="MyServer" 
      connectionTimeout="20000" 
      redirectPort="8443" 
      URIEncoding="UTF-8"/> 

Со временем конфигурации, я хотел бы заменить ключи со значениями из заданного файла свойств.

Проблема заключается в том, что требуется длительное время (несколько секунд в файле), чтобы перебирать все файлы и перебирать все свойства, чтобы проверить на замену для каждого свойства. Скрипт ниже.

Любая идея, как это сделать быстрее, используя bash?

Возможно, есть способ в sed или awk, чтобы получить все свойства как один вход?

Возможно, замена в памяти?

Сценарий:

for file in "${files[@]}" 
do 
    # echo ------ 
    #echo In file: $file 
    #echo ------ 
    cp -f "$file" "$file.bak" 
    unset replaced 
    for prop in "${!props[@]}" 
    do 
     key="$prop" 
     keyToReplace="\${$prop}" 
     val="${props[$key]}" 
     # echo In prop: $key=$val 
     sed -i "s|$keyToReplace|$val|Igw $file.change" "$file" 
     assertReturnStatus $? "sed failed" 
     if [ -z "$replaced" ] && [ -s "$file.change" ]; then 
      replaced=true 
      echo Replacing props in file "$file" 
      cp -n "$file.bak" "$file.orig" 
     fi 
    done 
    rm -f "$file.change" 
    rm -f "$file.bak" 
done 

* Одним из требований является создание резервной копии (.orig), если файл был изменен.

Спасибо.

+0

Рассмотрите возможность использования XML-парсер, как xmllint – anubhava

+0

@anubhava - Я предпочитаю, чтобы держать ключи/значения, просто, как я могу. Следовательно, файл свойств. –

+0

О, и файлы конфигурации, которые нужно заменить, могут быть любыми: от XML до JSON. –

ответ

1

Ваша петля внутри цикла заставляет sed запускать несколько раз для каждого файла. Итак, избавитесь от цикла внутри цикла.

declare -a sedArgs 

for prop in "${!props[@]}" 
do 
    key="$prop" 
    keyToReplace="\${$prop}" 
    val="${props[$key]}" 
    # echo In prop: $key=$val 
    #sed -i "s|$keyToReplace|$val|Igw $file.change" "$file" 
    sedArgs+=("-e") 
    sedArgs+=("s|$keyToReplace|$val|Ig") 
done 

for file in "${files[@]}" 
do 
    # echo ------ 
    #echo In file: "$file" 
    #echo ------ 
    sed -i.orig "${sedArgs[@]}" $file 
    cmp -s "$file" "${file}.orig" && mv "${file}.orig" "$file" 

done 
+0

Отлично. Я немного изменил его, чтобы эхо, если файл заменен: 'if cmp -s '$ file" "$ {file} .orig" ... ' –

+0

Это решение было проблематичным при запуске на gitbash (в окнах). Файл резервной копии (.orig) имел разные концы строк, чем оригиналы, что вызывало появление, как будто все файлы были заменены. –

+0

Вы можете использовать 'dos2unix' или' unix2dos' в созданном файле, чтобы изменить окончания строки. В качестве альтернативы может возникнуть вариант 'diff -strip-trailing-cr -q" $ file "" $ {file} .orig ">/dev/null'. – RTLinuxSW

0

в оперативной памяти решение, чтение содержимого в память и делать замены в памяти:

for file in "${files[@]}" 
do 
# echo ------ 
# echo In file: $file 
# echo ------ 
    unset replaced 
    contentOrig=`cat "$file"` 
    content="$contentOrig" 
    for prop in "${!props[@]}" 
    do 
     # No need to search for all properties if there are 
     if [[ ! "$content" == *'${'* ]]; then 
      break 
     fi 
     key="$prop" 
     keyToReplace="\${$prop}" 
     val="${props[$key]}" 
#  echo In prop: $key=$val 
     content="${content//$keyToReplace/$val}" 
     assertReturnStatus $? "sed failed" 
    done 
    # TODO: Show warning if `${` still exists (Excluding `*.sh` files). We might have forgotten to add a property. 
    if [[ "$content" != "$contentOrig" ]]; then 
     let "filesReplacedCount++" 
     cp -n "$file" "$file.orig" 
     echo "$content" > "$file" 
     echo Replaced props in file "$file" 
    fi 
done