如何从文件中删除与特定模式匹配的文本

如何从文件中删除与特定模式匹配的文本

我想比较两个文件以检查它们(fileA 和 fileB)之间的差异。 fileA 就像一个模板文件,fileB 是我要与之比较的文件。每当我发现差异时,我都想将该差异输出到 fileC。

困难在于 fileA 和 fileB 包含某些(不是全部)行,这些​​行的某些数据始终不同 - 时间、日期和随机生成的 ID 代码。但是,我不想将行输出到 fileC,其中唯一的区别是时间、日期和 id 代码。

所以我想做的是从 fileB 中出现的任何行中删除时间、日期和 id 代码(我可以在 fileA 中手动执行此操作),然后与 fileB 进行比较,将不同的行输出到 fileC。

请注意,要删除的文本始终遵循特定的模式。所以我可以使用 grep 和这些模式找到文本,但我不知道如何删除它......

这是两个文件的示例,以说明我的意思:

  • 文件B

    qaqa rara
    abc 10:12:25 08/20/2014 123456 def
    ghi fff ddd
    jkl 09:20:40 08/20/2014 978645 dfdf gggg
    
  • 文件A

    qaqa rara
    abc 10:32:15 07/15/2014 121456 xxx
    ghi eee ddd
    jkl 10:01:22 07/15/2014 971645 dfdf gggg
    

我想找到上述两个文件之间的差异,忽略时间(例如 10:12:25)、日期(例如 08/20/2014)或 id 代码(例如 123456)并将差异输出到 fileC

这两行不同,因此是第 2 行和第 3 行。两个文件的第 1 行相同。当时间、日期和 ID 信息被删除时,两个文件的第 4 行是相同的。

答案1

如果您的时间戳格式一致,您可以在使用任何差异方法处理文件之前将它们删除(例如使用 sed),例如

diff <(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileA) <(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileB)

对您提供的输入文件进行测试:

$ diff \
<(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileA) \
<(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileB)
2,3c2,3
< abc xxx
< ghi eee ddd
---
> abc def
> ghi fff ddd

答案2

获得结果的最简单命令如下

$ diff <(tr -s "[0-9],:,/" " " < 文件A) <(tr -s "[0-9],:,/" " " < 文件B)

该命令非常简单,也没有复杂的正则表达式。

示例输出如下

2,3c2,3
< abc xxx
< ghi eee ddd
---
> abc def
> ghi fff ddd

希望这是你想要的。

答案3

diff \
<(sed -r 's\[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{4} [0-9]{6} \\' fileA) \
<(sed -r 's\[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{4} [0-9]{6} \\' fileB) \
| egrep '^> ' | sed -r 's/^> //' > fileC

解释

从 fileA 和 fileB 中删除 OP 问题中给出的不相关部分,并将其输入 diff 中。

diff 将输出更改的部分,前面带有 "> ",因此忽略除更改之外的所有其他内容。

最后从输出中去除前导“>”并将其根据问题存储在 fileC 中。

我最初的做法略有不同,但我只是注意到文件可能在不相关的部分有所不同,因此必须预先剥离而不是后剥离,否则 diff 将输出仅在考虑相关部分时实际上未更改的信息。

给定示例输入,cat fileC给出:

abc def
ghi fff ddd

sed 命令正在搜索提供的描述不相关数据的正则表达式,并将其替换为空字符串 - 即删除它。

答案4

{   paste -d\| /dev/fd/3 /dev/fd/4 |
    sed '/\([^ ]*\) [0-9:/ ]*\(.*\)|\1 .*\2/d;=' |
    sed 'N;s/\(\n\)\(.*\)|/:\tFILEA: \2\1\tFILEB: /'
} 3<<\FILEA 4<<\FILEB
qaqa rara
abc 10:12:25 08/20/2014 123456 def
ghi fff ddd
jkl 09:20:40 08/20/2014 978645 dfdf gggg
FILEA
qaqa rara
abc 10:32:15 07/15/2014 121456 xxx
ghi eee ddd
jkl 10:01:22 07/15/2014 971645 dfdf gggg
FILEB

输出

2:      FILEA: abc 10:12:25 08/20/2014 123456 def
        FILEB: abc 10:32:15 07/15/2014 121456 xxx
3:      FILEA: ghi fff ddd
        FILEB: ghi eee ddd

你不需要去掉时间和日期——只要组成它们的字符是可靠的,它们就不是什么大障碍。

在上面的管道中,首先使用单个分隔符paste将相应的行 from 附加FILEB到每行的尾部,然后将结果打印到。FILEA|stdout

sed拾取流并比较:

  • 第一个由 0 个或多个非空格字符组成的序列(引用为\1

  • 出现在以下序列之间的所有字符:(引用为\2

    • 至少一个<space>字符,然后是 0 个或多个以下任意字符:

    • <space>人物

    • <digit>人物

    • <:colon>人物

    • </slash>人物

  • 直到但不包括|该行最后出现的字符

...和|\1.*\2​​。如果它们匹配则sed删除该行。如果不是,它会打印该行,并在其行号之前打印该行。

最后的sed过程只是美化输出(我希望)

相关内容