Bash 通过有条件地覆盖第二个文件的部分来合并两个文件
我有两个配置文件,旧的和新的。新的是一个模板配置,带有默认值,可能还有其他变量。旧配置被修改,其值必须保留。我需要做一个新的修改配置:Bash 通过有条件地覆盖第二个文件的部分来合并两个文件,bash,awk,sed,diff,Bash,Awk,Sed,Diff,我有两个配置文件,旧的和新的。新的是一个模板配置,带有默认值,可能还有其他变量。旧配置被修改,其值必须保留。我需要做一个新的修改配置: 若旧版本和新版本中都存在变量,则保留旧值 若变量在old中被注释掉,那个么将其保留,反之亦然 如果变量仅存在于旧版本中,请将其删除 若变量只存在于new中,则保留它 旧改性 $ cat old.cfg # var1 = 111 # var2 = 123 var3 = 111 var4 = 123 var5 = 123 新的默认配置 $ cat new_defa
$ 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
新修改的配置(需要)
#
总是用空格隔开(因为配置编辑是手动的,所以我使用sed
来实现:sed-i-E的/^#([^])/#\1/”
),所以也许整个过程都可以使用awk。现在我有了这个awk'FNR==NR{a[$1]++;next}!一个[$1]'new_default.cfg old.cfg
,用于写下变量名(awk中的第1列),这两个文件都通用
===================================================
UPD:
最后,我使用了下面的答案,并对其进行了修改,使其更好地满足了我的需求,而且看起来更丑
#
后面有空格=
每侧都有一个空间#
开始,而不是一个#
->开头,则比较第二列;
从##
->开始比较整行;开始时不使用#
->先比较
纵队如果您想实现与特定于应用程序的逻辑的合并,那么获得所需逻辑的唯一可靠方法就是亲自构建它。因此:
#!/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
#/usr/bin/env bash
“|[123].*)echo”中的case$BASH_版本错误:需要BASH 4.0或更高版本“>&2;出口1;;以撒
宣布-旧的新的
读取到数组(){
本地线路
本地-n目的地=1美元
本地-n注释目的地=$2
声明-g-A“$1”“$2”
当IFS=读取-r行时;执行
案例$line in
“”继续;;
“#”*“=”*)行=${line#“#”};
注释_dest[${line%%”=“*}]=$line;;
“#”*)继续;;
*“=”*)dest[${line%%”=“*}]=${line#*”=“};”;;
*)echo“忽略未识别的行:$line”>&2
以撒
完成
}
请阅读“对数组的旧注释”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
但是,不确定示例输入/输出中是否包含所有测试。此答案假设您在#
和=
之间始终有空格,例如在示例输入中:
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
如果这是重复的或者我没有说清楚,我也会事先道歉。多年来一直以只读模式出现在网站上,但今天却完全被sed、awk、diff、patch等搞糊涂了(:哇,伙计们,你们太棒了!这绝对是你们所有人的精彩输入,非常感谢!
$ 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
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