我想比较两个文件以检查它们(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
过程只是美化输出(我希望)。