如果 "file" 具有空分隔项,如何使用 "grep -f file"?
How to use "grep -f file" if "file" has null-delimited items?
我需要从存在于 data1
中的众多文件(data2
、data3
、...)中找到以空分隔的项目。需要完全匹配。
在 data1
中的项目也用 null 分隔之前,grep -f data1 data2 data3 ...
一切正常。
只使用换行符 - ok:
$ cat data1
1234
abcd
efgh
5678
$ cat data2
1111
oooo
abcd
5678
$ grep -xFf data1 data2
abcd
5678
data2
包含空分隔项 - ok 当 -z
使用时:
$ printf '1111[=13=]oooo[=13=]abcd[=13=]05678' > data2
$ grep -zxFf data1 data2 | xargs -0 printf '%s\n'
abcd
5678
现在 data1
和 data2
都包含空分隔项 - 失败。 -z
选项似乎不适用于 -f
:
指定的文件
$ printf '1234[=14=]abcd[=14=]efgh[=14=]05678' > 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
)?
由于订单保留并不重要,您正在尝试匹配精确的字符串,并且您有可用的 GNU 工具,而不是使用 fgrep
我建议 comm -z
.
$ printf '%s[=10=]' 1111 oooo abcd 005678 >data2
$ printf '%s[=10=]' 1234 abcd efgh 005678 >data
$ comm -z12 <(sort -uz <data) <(sort -uz <data2) | xargs -0 printf '%s\n'
005678
abcd
如果您首先生成排序的文件(因此可以省略 sort
操作),这也将具有非常好的内存和性能特征。
(虽然以下可能不是针对这种特殊情况的最佳解决方案,但我还是添加了它,以防它有助于将来 reader 遇到类似问题。请参阅下面的 gawk
解决方案这可能对这个用例有用。)
grep
将换行符硬连接为模式终止符。即使您使用 -e pattern
,模式字符串中的换行符也会导致 grep 将选项处理为指定多个模式,而不是包含换行符的单个模式。
但是,如果您的 NUL 分隔模式不包含换行符,您可以使用 Gnu xargs
和 sed
构造一个适当的 grep
调用 -e
命令行参数:
sed -z 's/^/-e/' data | xargs -0 grep -zF data2 ...
(这是有效的,因为 Gnu grep
重新排列了命令行参数,所以可以将要搜索的文件放在模式之前。这对许多其他 grep
实现不起作用。 )
据我所知,对于可能包含换行符的模式没有解决方法。 grep -E
和 grep -F
不识别 ascii 转义序列,并且会从包含换行符的模式中默默地创建多个模式。 grep -P
(另一个使用 PCRE regexen 的 Gnu 扩展)将正确处理嵌入的换行符或 ascii 转义符,但只允许单一模式。
不排序的全行 NUL 终止匹配
如果您只对精确的、完整的“行”匹配感兴趣 (-Fx
),您可以使用 Gnu Awk 脚本而不是对输入和模式进行排序。对于无法放入内存的非常大的输入,这可能是一个胜利;使用外部临时文件进行排序可能会非常昂贵。 Awk 解决方案使用散列 table,因此不需要排序。 (同样,这可能不适用于所有 Awk,因为它依赖于将 RS
设置为 NUL。)
awk -v RS=`[=11=]` 'NR==FNR{p[[=11=]] = 1; next;} [=11=] in p' data data2 ...
我需要从存在于 data1
中的众多文件(data2
、data3
、...)中找到以空分隔的项目。需要完全匹配。
在 data1
中的项目也用 null 分隔之前,grep -f data1 data2 data3 ...
一切正常。
只使用换行符 - ok:
$ cat data1 1234 abcd efgh 5678 $ cat data2 1111 oooo abcd 5678 $ grep -xFf data1 data2 abcd 5678
data2
包含空分隔项 - ok 当-z
使用时:$ printf '1111[=13=]oooo[=13=]abcd[=13=]05678' > data2 $ grep -zxFf data1 data2 | xargs -0 printf '%s\n' abcd 5678
现在
指定的文件data1
和data2
都包含空分隔项 - 失败。-z
选项似乎不适用于-f
:$ printf '1234[=14=]abcd[=14=]efgh[=14=]05678' > 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
)?
由于订单保留并不重要,您正在尝试匹配精确的字符串,并且您有可用的 GNU 工具,而不是使用 fgrep
我建议 comm -z
.
$ printf '%s[=10=]' 1111 oooo abcd 005678 >data2
$ printf '%s[=10=]' 1234 abcd efgh 005678 >data
$ comm -z12 <(sort -uz <data) <(sort -uz <data2) | xargs -0 printf '%s\n'
005678
abcd
如果您首先生成排序的文件(因此可以省略 sort
操作),这也将具有非常好的内存和性能特征。
(虽然以下可能不是针对这种特殊情况的最佳解决方案,但我还是添加了它,以防它有助于将来 reader 遇到类似问题。请参阅下面的 gawk
解决方案这可能对这个用例有用。)
grep
将换行符硬连接为模式终止符。即使您使用 -e pattern
,模式字符串中的换行符也会导致 grep 将选项处理为指定多个模式,而不是包含换行符的单个模式。
但是,如果您的 NUL 分隔模式不包含换行符,您可以使用 Gnu xargs
和 sed
构造一个适当的 grep
调用 -e
命令行参数:
sed -z 's/^/-e/' data | xargs -0 grep -zF data2 ...
(这是有效的,因为 Gnu grep
重新排列了命令行参数,所以可以将要搜索的文件放在模式之前。这对许多其他 grep
实现不起作用。 )
据我所知,对于可能包含换行符的模式没有解决方法。 grep -E
和 grep -F
不识别 ascii 转义序列,并且会从包含换行符的模式中默默地创建多个模式。 grep -P
(另一个使用 PCRE regexen 的 Gnu 扩展)将正确处理嵌入的换行符或 ascii 转义符,但只允许单一模式。
不排序的全行 NUL 终止匹配
如果您只对精确的、完整的“行”匹配感兴趣 (-Fx
),您可以使用 Gnu Awk 脚本而不是对输入和模式进行排序。对于无法放入内存的非常大的输入,这可能是一个胜利;使用外部临时文件进行排序可能会非常昂贵。 Awk 解决方案使用散列 table,因此不需要排序。 (同样,这可能不适用于所有 Awk,因为它依赖于将 RS
设置为 NUL。)
awk -v RS=`[=11=]` 'NR==FNR{p[[=11=]] = 1; next;} [=11=] in p' data data2 ...