我有一个大的缩小text.json
文件,我需要在其中找到一个特定的短语dasdhfb347rbf
并打印出周围的短语,例如 100 个字符的上下文(前导/尾随)。
我已经尝试过grep -Eo '.{0,100}dasdhfb347rbf.{0,100}' /dir/text.json
但它似乎永远悬而未决。
附言。我有一台带有i7 CPU,8GB RAM和SSD驱动器的Macbook。
正如 Mark Setchell 在他的回答中指出的那样,您在.{0,100}
中使用(未转义的){
和}
来匹配多达 100 个字符。 需要使用-E
来启用扩展正则表达式(正则表达式); 或者,您可以使用转义(使用默认的基本正则表达式):.{0,100}
.
但是,这两种更正都不能解决您的问题,这是一个性能问题:grep
,整个文件 - 因为它是缩小的JSON--是一行,并且您的特定正则表达式会导致惊人的长执行时间(取决于您的硬件,大约10 +分钟与600MB文件;大概,您的正则表达式需要大量的回溯)。
使用LC_ALL=C
,正如Yreg的回答所建议的那样,带来了轻微的改进,但不足以产生真正的差异(LC_ALL=C
简化了字符处理,因为每个字节都被假定为ASCII字符)。
切换到文本字符串匹配可显著提高性能,但虽然grep
确实支持文本匹配,但它不支持报告基于字符的上下文(仅基于行)。
因此,要使用的工具是awk
,它提供了用于文字字符串匹配和基于位置的子字符串提取的函数:
awk -v RS='3' -v txt='dasdhfb347rbf' -v n=100 '
BEGIN {
getline; s = $0 # read the entire file
while (pos=index(s, txt)) { # loop over matches
len = length(txt) + 2 * n - (pos - n < 1 ? n - pos + 1 : 0)
print substr(s, pos-n, len)
s = substr(s, pos -n + len)
}
}
' text.json
以上应该表现得更好。
请注意,需要v RS='3'
才能使 BSD Awk 一次读取整个文件(-v RS='^$'
通常用于 GNU Awk 和 Mawk,但这不适用于 BSD Awk);请注意,这种技术依赖于控制字符0x3
而不是文本的一部分。
附带说明一下:
GNUAwk,你可以通过Homebrew安装,比我机器上的BSD Awk快两倍多。
Mawk(也可以通过Homebrew安装)通常是最快的Awk,在这里不是一个选项,因为它似乎对行长度有硬性限制。
尝试更改区域设置。
LC_ALL=C grep -o…
我认为您需要在macOS上使用-E
选项来允许使用扩展正则表达式:
# without "-E", finds nothing
echo abcdefghijk | grep -o ".def.{3}"
# with "-E", finds regex
echo abcdefghijk | grep -Eo ".def.{3}"
cdefghi