6

我需要从存在data2于. 需要精确匹配。data3data1

一切都很好,grep -f data1 data2 data3 ...直到其中的项目data1也以空值分隔。

  1. 仅使用换行符 -好的

    $ cat data1
    1234
    abcd
    efgh
    5678
    $ cat data2
    1111
    oooo
    abcd
    5678
    $ grep -xFf data1 data2
    abcd
    5678
    
  2. data2包含以 null 分隔的项目 -使用-z可以:

    $ printf '1111\0oooo\0abcd\0005678' > data2
    $ grep -zxFf data1 data2 | xargs -0 printf '%s\n'
    abcd
    5678
    
  3. 现在两者data1data2都包含以空分隔的项目 -失败。似乎该-z选项不适用于指定的文件-f

    $ printf '1234\0abcd\0efgh\0005678' > data1
    $ grep -zxFf data1 data2 | xargs -0 printf '%s\n'
    
    $
    

问题是我确实需要两个文件都有以空分隔的项目。明显的解决方法可能是(例如)一个很好的旧while循环:

while IFS= read -rd '' line || [[ $line ]]; do
    if grep -zqxF "$line" data2; then
        printf '%s\n' "$line"
    fi
done < data1

但是由于我有很多包含很多项目的文件,这将非常缓慢!有没有更好的方法(我不坚持使用grep)?

4

2 回答 2

6

由于订单保留并不重要,因此您尝试匹配确切的字符串,并且您有可用的 GNU 工具,而不是使用fgrep我建议的comm -z.

$ printf '%s\0' 1111 oooo abcd 005678 >data2
$ printf '%s\0' 1234 abcd efgh 005678 >data
$ comm -z12 <(sort -uz <data) <(sort -uz <data2) | xargs -0 printf '%s\n'
005678
abcd

如果您首先生成已排序的文件(因此可以省略sort操作),这也将具有非常好的内存和性能特征。

于 2018-08-28T15:25:05.250 回答
2

(虽然以下可能不是这个特殊情况的最佳解决方案,但我还是添加了它,以防它帮助未来的读者遇到类似问题。请参阅下面的gawk解决方案,这可能对这个用例有用。)

grep已硬连线换行作为模式终止符。即使您使用-e pattern,模式字符串中的换行符也会导致 grep 将选项处理为指定多个模式,而不是包含换行符的单个模式。

但是,如果您的 NUL 分隔模式不包含换行符,您可以使用 Gnuxargs并使用命令行参数sed构造适当的grep调用:-e

sed -z 's/^/-e/' data | xargs -0 grep -zF data2 ...

(这是可行的,因为 Gnugrep会重新调整命令行参数,因此可以将要搜索的文件放在模式之前。这在许多其他实现中不起作用grep。)

据我所知,对于可能包含换行符的模式没有解决方法。grep -E并且grep -F不识别 ascii 转义序列,并且会从包含换行符的模式中默默地创建多个模式。grep -P(另一个使用 PCRE regexen 的 Gnu 扩展)将正确处理嵌入的换行符或 ascii 转义,但只允许单个模式。


没有排序的全行 NUL 终止匹配

如果您只对精确的完整“行”匹配 ( -Fx) 感兴趣,则可以使用 Gnu Awk 脚本而不是对输入和模式进行排序。对于不适合内存的非常大的输入,这可能是一个胜利;使用外部临时文件进行排序可能会非常昂贵。awk 解决方案使用哈希表,因此不需要排序。(同样,这可能不适用于所有 Awks,因为它依赖于设置RS为 NUL。)

awk -v RS=`\0` 'NR==FNR{p[$0] = 1; next;} $0 in p' data data2 ...
于 2018-08-28T17:59:04.293 回答