Объединение двух файлов путем условного переопределения частей второго файла - PullRequest
0 голосов
/ 06 июня 2018

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

  1. , если переменная существует как в старой, так и в новой версии, сохранить старое значение
  2. , если переменная закомментирована в старой, сохранить ее и наоборот.наоборот
  3. если переменная существует только в старом, удалите его
  4. , если переменная существует только в новом, сохраните его

старый измененный

$ cat old.cfg
# var1 = 111
# var2 = 123
var3 = 111
var4 = 123
var5 = 123

новая конфигурация по умолчанию

$ cat new_default.cfg
var1 = 111
# var2 = 123
var3 = 111
# var4 = 111
var6 = 111

новая измененная конфигурация (желательно)

$ cat new.cfg
# var1 = 111
# var2 = 123
var3 = 111
var4 = 123
var6 = 111

# всегда разделяется пробелами (так как редактирование конфигурации выполняется вручную, я использую sed дляthis: sed -i -E 's/^#([^ ])/# \1/'), так что, возможно, awk можно использовать для всего этого.На данный момент у меня есть awk 'FNR==NR{a[$1]++;next}!a[$1]' new_default.cfg old.cfg, который записывает имя переменной (1-й столбец в awk), что является общим для обоих файлов.

========================================================

UPD: Наконец-то я воспользовался приведенным ниже ответом и изменил его, так что теперь он лучше отвечает моим потребностям, а также выглядит уродливее.

  1. , принимая два аргумента,старый конфиг и шаблон для пропатченного конфига
  2. , обеспечивающий следующий пробел после # в начале строки
  3. , гарантирующий, что каждый = окружен одним пробелом с каждой стороны
  4. гарантирует, что каждый фактический комментарий начинается с двух # вместо одной
  5. , выдающей команду awk: если строка начинается с # -> сравнить второй столбец;начинается с ## -> сравнить всю строку;начинается без # -> сравнить первый столбец

.

#!/bin/bash
for var in "$@"
do
    cp $var $var.bak
    sed -i -E 's/^#([^ ])/# \1/' $var
    sed -i -E 's/(.?)(\s?)=(\s?)(.?)/\1 = \4/' $var
    sed -i -E 's/^#([^=]+)$/##\1/' $var
done
awk '{if(/^# /)k=$2;else if(/^## /)k=$0;else k=$1;}NR==FNR{a[k]=$0; next} 
{print (k in a)?a[k]:$0}' $1 $2 > output.txt

Ответы [ 3 ]

0 голосов
/ 06 июня 2018

awk на помощь!

$ awk       '{k=/^#/?$2:$1} 
     NR==FNR {a[k]=$0; next}
             {print (k in a)?a[k]:$0}' config.old config.new

# var1 = 111
# var2 = 123
var3 = 111
var4 = 123
var6 = 111

не уверен, что все тесты включены в ваш пример ввода / вывода.

0 голосов
/ 06 июня 2018

В этом ответе предполагается, что у вас всегда есть пробелы вокруг # и =, как в примере ввода:

awk '
    NR == FNR {if ($1 == "#") new_ignore[$2]; else new[$1] = $3; next }
    $1 == "#" { delete new[$2]; print; next }
    { old[$1] }
    $1 in new || $1 in new_ignore { print; next }
    END { for (key in new) if (!(key in old)) printf "%s = %s\n", key, new[key] }
' new_default.cfg old.cfg
# var1 = 111
# var2 = 123
var3 = 111
var4 = 123
var6 = 111
0 голосов
/ 06 июня 2018

Если вы хотите реализовать слияние с логикой для конкретного приложения, единственный верный способ получить желаемую логику - это на самом деле создать ее самостоятельно.Таким образом:

#!/usr/bin/env bash
case $BASH_VERSION in ''|[123].*) echo "ERROR: Bash 4.0 or newer required" >&2; exit 1;; esac

declare -A old new

read_to_array() {
  local line
  local -n dest=$1
  local -n comment_dest=$2
  declare -g -A "$1" "$2"
  while IFS= read -r line; do
    case $line in
      "")          continue;;
      "#"*" = "*)  line=${line#"#"};
                   comment_dest[${line%%" = "*}]=$line;;
      "#"*)        continue;;
      *" = "*)     dest[${line%%" = "*}]=${line#*" = "};;
      *)           echo "Ignoring unrecognized line: $line" >&2
    esac
  done
}

read_to_array old old_comments <old.txt
read_to_array new new_comments <new.txt
declare -A done=( )

for key in "${!new[@]}"; do
  # if commented out in old, leave it that way
  if [[ ${old_comments[$key]} ]]; then
    echo "#$key = ${new[$key]}"
    continue
  fi
  # key exists in both old and new; use old
  if [[ ${old[$key]} ]]; then
    echo "$key = ${old[$key]}"
    continue
  fi
  # key is only in new; keep it
  echo "$key = ${new[$key]}"
done

for key in "${!new_comments[@]}"; do
  # if present at all in old, we were already emitted
  [[ ${old[$key]} ]] && continue
  echo "${new_comments[$key]}"
done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...