比较行并升级两个不同的文件

比较行并升级两个不同的文件

我有几个具有以下功能的文本文件:

$ cat file_1
Line A
Line B
Line C
Line D

$ cat file_2
Line A
Line added 1
Line B
Line D
Line added 2

它们file_1有一些file_2不包含的行,反之亦然。我想升级彼此丢失的行,这样两者都会变成

Line A
Line added 1
Line B
Line C
Line D
Line added 2

命令offile_1被保留,但来自file_2put 的积分与 in 相同的位置file_2(不是在头部、尾部或随机位置)。

1)是否可以通过适当的脚本以这种方式合并文件bash

2)当我用段落而不是行(即:行块)时,是否可以做同样的事情?

答案1

diff file_1 file_2 | grep -Ev '^<|[0-9]+d[0-9]+' | patch file_1

答案2

1)是否可以通过适当的 bash 脚本以这种方式合并文件?

正如其他评论者所说,区分添加/移动/删除并不精确,最好留给diff.但由于“原始”行是唯一的,并且在两个文件中以相同的顺序出现的限制,它更加简单:

#!/bin/bash

#build list of common lines
grep -Fxf file_2 file_1 > common

#optional: confirm that they appear in the same order in both files
grep -Fxf file_1 file_2 > common2
if ! diff -q common common2 ; then
    echo "Duplicate or rearranged common lines, can't merge" >&2
    exit 1
fi

#copy lines from input until one is found that matches the argument
function copy_till () {
    while read l && [ "$l" != "$1" ] ; do
            printf "%s\n" "$l"
    done
}

# open both files, for parallel reading
exec 3< file_1
exec 4< file_2

#for each line in the common file
while read line ; do
    # copy any lines that were inserted before it, for each input file
    copy_till "$line" <&3
    copy_till "$line" <&4
    # and the original line
    printf "%s\n" "$line"
done < common > merged

# any trailing lines, after the last common line
cat <&3 >> merged
cat <&4 >> merged

2)当我用段落而不是行(即:行块)时,是否可以做同样的事情?

一旦您找到了对行执行此操作的方法,您就可以sed将段落转换为长行(将换行符保存为其他临时标记)并返回。大量借用了注释脚本https://unix.stackexchange.com/a/219562/90751:

sed '/^$/!{H;$ba;d};:a;x;s/\n/\\n/gp;d' -i.bak file_1
sed '/^$/!{H;$ba;d};:a;x;s/\n/\\n/gp;d' -i.bak file_2
merge.sh # or whatever you called the answer to part 1)
sed 's/\\n/\n/g' merged > merged.paras

mv file_1.bak file_1
mv file_2.bak file_2

如果该字符串\n出现在您的段落中,请使用另一个字符串作为换行符。

答案3

1)是否可以通过适当的bash脚本以这种方式合并文件?

假如:

  • file_2基本上是file_1添加和删除的行但没有移动, 和
  • 两个文件都不包含制表符,

GNUdiff提供了简单解决方案的关键:

#!/usr/bin/env bash
diff -y "$1" "$2" | while IFS=$'\t' read s1 s2 s3; do
  if [[ $s1 == *\> ]]; then
    # New line from second file
    echo "$s2"
  else
    case "$s2" in
      *\|) # Changed line, so...
        echo "$s1"
        echo "$s3"
      ;;
      *) # Output first file
        echo "$s1"
      ;;
    esac
  fi
done

这会将统一的输出发送到标准输出,您可以在其中执行任何您想要的操作。我建议首先检查输出,而不是盲目地覆盖file_1file_2

(如果您想知道这是如何工作的,请运行diff -y file_1 file_2 | cat -A以查看输出中的实际内容diff。)

2)当我用段落而不是行(即:行块)时,是否可以做同样的事情?

是的。基本逻辑与上面相同,但是你必须首先将段落转换为单行并使用重新格式化的文本作为上述的输入diff。然后您有两个输出选项:

  • 保留原始段落。从正确的文件中读取每个段落并输出。这是给你的练习。
  • 生成新段落。echo上面脚本中的每个都变成echo ... | fmt -w<desire_line_width>.

答案4

这本质上是执行一个问题2种方法或者双向合并两个文件之间同步它们之间的差异。我自己的用例是在 GitHub 存储库上执行升级,并在变量文件中保留唯一数据,以防止在每次升级时重新输入密钥!

我将首先向您展示表达式和使用的测试数据,以便您可以重新创建结果进行验证(只需将数据集的字段分隔符从“=”更改为空格“”):

 paste -d'\n' file1.txt file2.txt|awk -F'=' '!seen[$1]++' > file3.txt

我的测试数据如下所示:

文件1.txt:

LineA='value1'
LineB='value2'
LineC='value3'
LineD='value4'
#
LineE='value5'
LineF='value6'
#
LineG='value7'
#
LineH='value8'

文件2.txt:

LineA=''
LineB=''
NEWVARIABLE1='This only Exists in file2.txt Under LineB'
LineC=''
LineD=''
#
LineE=''
NEWVARIABLE2='This only Exists in file2.txt Under LineE'
LineF=''
#
LineG=''
#
LineH=''
NEWVARIABLE3='This only Exists in file2.txt under LineH'

输出:

paste -d'\n' file1.txt file2.txt|awk -F'=' '!seen[$1]++' > file3.txt

组合文件3.txt看起来像这样:

LineA='value1'
LineB='value2'
LineC='value3'
NEWVARIABLE1='This only Exists in file2.txt Under LineB'
LineD='value4'
#
LineE='value5'
LineF='value6'
NEWVARIABLE2='This only Exists in file2.txt Under LineE'
LineG='value7'
LineH='value8'

NEWVARIABLE3='This only Exists in file2.txt under LineH'

在文件中注明2.txt 所有值均为空 (''),除了新变量x价值观。查看输出,您可以看到文件中的所有唯一数据1合并后.txt 已被保留。

另请注意,添加到 file2.txt(“更新的”文件)的每个新“变量”在合并的 file3.txt 中减少 1 行。因此,如果您向 file2.txt 添加 4 个新变量,则在组合的 file3.txt 中,第四个新变量将比 file3.txt 低 4 行。然而,在我的用例中这不是问题。

遗憾的是,这将适用于您的第一个用例 - 执行2路文件合并。但是,如果您向 file2.txt 添加连续的测试块(同样是“更新的文件”),这些测试块将与文件中的周围行交错1合并文件中的.txt3。TXT。所以我已经帮你解决了一半,但我的用例只与你问题的第一部分重叠。

我尝试了这个论坛和其他论坛上的其他发帖者提供的大量不同的解决方案,但这确实是唯一能做到这一点的解决方案。 HTH-

相关内容