打印文件中未排序的字符串之间的差异

时间:2019-04-17 11:24:25

标签: bash comm

我有两个文件,每个文件包含n行,每行都有一个字符串。我想打印出这些列表之间的字符差异。您可以将操作想象为字母的“减法”。它应该是这样的:

List1       List2      Result
AaBbCcDd    AaCcDd     Bb
AaBbCcE     AaBbCc     E
AaBbCcF     AaCcF      Bb

这意味着第二个列表不是按字母顺序排序的,而是所有要删除的子字符串都在每个字符串中排序(AaBb之前在Cc之前)。请注意,要删除的元素可以是1个或2个字符长(AaF),始终以大写字母开头(有时)以小写字母开头。字符串完全由几个“元素”的排列组成,例如AaBbCcDdEFGg,等等。

此问题的回答方式非常相似: Bash script Find difference between two strings, 但仅适用于手动输入的两个字符串,而我需要执行数百次操作。我正在努力将文件实现为该命令的源,同时还要正确分隔字符。这是我的改编:

split_chars() { sed $'s/./&\\\n/g' <<< "$1"; }
comm -23 <(split_chars AaBbCcDd) <(split_chars AaCcDd)

给出输出

B
b

即使在这种情况下,也仍然不是我想要的。我猜想split_chars命令是这里的关键,但是我无法以任何方式将其应用于我的文件。将文件名放在方括号中显然不起作用。 作为参考,一个简单的

commm -23 List1 List2

只是导致

AaBbCcDd
AaBbCcEe
AaBbCcF
comm: file 2 is not in sorted order

2 个答案:

答案 0 :(得分:1)

由于您不想拆分字符,而是要以大写字母开头的子字符串,因此应将split_chars替换为以下函数。

split() { sed 's/[A-Z]/\n&/g' <<< "$1"; }

使用tr -d \\n删除所有换行符可以撤消拆分行。

要从另一行列表中减去行列表,可以使用grep而不必进行排序。

grep -vFxf subtrahend minuend

这将以原始顺序打印文件minuend中不在文件subtrahend中的行。

要将所有内容放在一起,您必须

  • 并行读取两个文件
  • 将每个字符串分成行列表
  • 减去这些列表
  • 撤消拆分

这是一个简化的版本,假设您的输入文件仅包含描述格式的行并且具有相同的长度。

split() { sed 's/[A-Z]/\n&/g' <<< "$1"; }
subtract() { grep -vFxf "$2" "$1"; }
union() { tr -d \\n; echo; }
paste List1 List2 | while read -r minuend subtrahend; do
    subtract <(split "$minuend") <(split "$subtrahend") | union
done

带有循环的Bash脚本很慢。如果您需要更快的解决方案,则应使用更高级的语言(例如perlpython重写此脚本。

答案 1 :(得分:0)

GNU awk中的另一个:

$ gawk 'NR==FNR {
    a[FNR]=$0
    next
}
{
    patsplit($0 a[FNR],b,/[A-Z][a-z]?/)
    printf "%s%s%s", a[FNR],OFS,$0
    for(i in b)
        if(!(match($0,b[i])&&match(a[FNR],b[i])))
            printf "%s%s", OFS, b[i]
    print ""
}' file1 file2

输出:

List1 List2
AaBbCcDd AaCcDd Bb
AaBbCcE AaBbCc E
AaBbCcF AaCcF Bb