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

Print differences between not sorted strings from files

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

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

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

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

split_chars() { sed $'s/./&\\n/g' <<< ""; }
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

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

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

可以通过使用 tr -d \n.

删除所有换行符来撤消拆分行的操作

要从另一个行列表中减去一个行列表,您可以使用 grep 而无需排序。

grep -vFxf subtrahend minuend

这将按原始顺序打印文件 minuend 中不在文件 subtrahend.

中的那些行

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

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

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

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

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

GNU awk 中的另一个:

$ gawk 'NR==FNR {
    a[FNR]=[=10=]
    next
}
{
    patsplit([=10=] a[FNR],b,/[A-Z][a-z]?/)
    printf "%s%s%s", a[FNR],OFS,[=10=]
    for(i in b)
        if(!(match([=10=],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