匹配前后的字符使用Grep命令如何实现?

215

使用以下方法:

grep -A1 -B1 "test_pattern" file

会在文件中匹配到的内容前后各产生一行。是否有一种方法可以显示指定数量的字符而不是行数?

我的文件中的行很长,所以我不想打印整行,而是只想观察上下文中的匹配。有没有关于如何做到这一点的建议?


1
重复的内容 https://unix.stackexchange.com/q/163726 与 https://dev59.com/ZnI-5IYBdhLWcg3wCz7y 几乎相同。 - sondra.kinsey
10个回答

279

前3个字符和后4个字符

$> echo "some123_string_and_another" | grep -o -P '.{0,3}string.{0,4}'
23_string_and

8
处理少量数据时,这是一个很好的答案,但当你匹配超过100个字符时就开始变慢了 - 例如在我的巨型XML文件中,我想要{1,200}之前和之后的内容,但使用它太慢了。 - Benubird
4
@amit_g的awk版本更快。 - ssobczak
13
在Mac OSX上不可用,因此这不是一种广泛可用的解决方案。下面列出的-E版本是更好的解决方案。-P是什么?请继续阅读...-P,--perl-regexp将PATTERN解释为Perl正则表达式(PCRE,请参见下文)。这是高度实验性的,grep -P可能会警告未实现的功能。 - Xofo
3
在OSX上安装,请使用命令brew install homebrew/dupes/grep,然后以ggrep的形式运行它。 - kenorb
1
正如@Benubird所暗示的那样,对于需要匹配目标周围适度宽广的大型文件来说,这将在性能上变得不可能使用。 - matanster
显示剩余4条评论

171
grep -E -o ".{0,5}test_pattern.{0,5}" test.txt 

这将匹配你的模式前后最多5个字符。-o开关告诉grep仅显示匹配项,-E表示使用扩展正则表达式。确保在你的表达式周围加上引号,否则可能会被shell解释。


4
好的答案,有趣的是在{}中长度被限制为2^8-1,所以{0,255}有效,而{0,256}会出现grep: invalid repetition count(s)的错误。 - CodeMonkey
4
随着匹配字符数量的增加(5->25->50),性能似乎显著下降,有什么想法吗? - Adam Hughes

50

你可以使用

awk '/test_pattern/ {
    match($0, /test_pattern/); print substr($0, RSTART - 10, RLENGTH + 20);
}' file

3
即使处理较大文件时,它仍能良好运行。 - Touko
6
如何使用这个工具来在一行中找到多个匹配项? - koox00
2
花括号中的第一个数字有什么意义?比如在“grep -E -o”.{0,5}test_pattern.{0,5}" test.txt"中的0是什么意思? - Lew Rockwell Fan
它确实更快,但不如@ekse的答案准确。 - Abdollah
对于大文件来说,这个命令根本不准确。对于一个5.5GB的文件,我确定其中有数百万个匹配项,但这个命令只返回了一个结果。 - duplex143
对于大文件来说,这个命令根本不准确。对于一个5.5GB的文件,我确定其中有数百万个匹配项,但这个命令只返回了一个结果。 - undefined

34
你的意思是这样吗:
grep -o '.\{0,20\}test_pattern.\{0,20\}' file

这将在test_pattern两侧打印最多二十个字符。 \{0,20\} 符号类似于 *,但它指定了零到二十个重复,而不是零或多个。-o 表示仅显示匹配本身,而不是整行。


这个命令对我没用:grep: Invalid content of \{\} - Alexander Pravdin
@AlexanderPravdin 我认为他假设grep是BRE(因此没有-E也没有-P)。如果它是ERE,则语法更简单。同样,如果它是PCRE,则与ERE的语法相同。您还可以执行echo zzzabczzzz | grep -o -P '.abc..',添加或删除任意数量的点。 - barlop

3

我永远不会容易地记住这些晦涩的命令修饰符,所以我采用顶级答案并将其转化为我的~/.bashrc文件中的一个函数:

cgrep() {
    # For files that are arrays 10's of thousands of characters print.
    # Use cpgrep to print 30 characters before and after search pattern.
    if [ $# -eq 2 ] ; then
        # Format was 'cgrep "search string" /path/to/filename'
        grep -o -P ".{0,30}$1.{0,30}" "$2"
    else
        # Format was 'cat /path/to/filename | cgrep "search string"
        grep -o -P ".{0,30}$1.{0,30}"
    fi
} # cgrep()

这是实际运行时的样子:

$ ll /tmp/rick/scp.Mf7UdS/Mf7UdS.Source

-rw-r--r-- 1 rick rick 25780 Jul  3 19:05 /tmp/rick/scp.Mf7UdS/Mf7UdS.Source

$ cat /tmp/rick/scp.Mf7UdS/Mf7UdS.Source | cgrep "Link to iconic"

1:43:30.3540244000 /mnt/e/bin/Link to iconic S -rwxrwxrwx 777 rick 1000 ri

$ cgrep "Link to iconic" /tmp/rick/scp.Mf7UdS/Mf7UdS.Source

1:43:30.3540244000 /mnt/e/bin/Link to iconic S -rwxrwxrwx 777 rick 1000 ri

所涉及的文件是一条连续的25K行,使用普通的grep无法找到您所需要的内容。

请注意,有两种不同的方式可以调用cgrep,类似于grep方法。

有一种更加妙手的创建函数方式,只有在设置了"$2"时才会传递,这将节省4行代码。我现在没有方便的方式。类似于${parm2} $parm2 如果我找到它,我将修改函数和这个答案。


1
如果使用ripgrep,您可以这样做:
grep -E -o ".{0,5}test_pattern.{0,5}" test.txt 

你的意思是 ripgrep,我想知道它和 grep 有什么不同?你的回答似乎与 ekse 的完全相同,除了 ripgrep 的规范。 - chrslg

0
使用 gawk,您可以使用 match 函数:
    x="hey there how are you"
    echo "$x" |awk --re-interval '{match($0,/(.{4})how(.{4})/,a);print a[1],a[2]}'
    ere   are

如果你熟悉perl,那么更加灵活的解决方案是:在实际模式之前打印出模式之前的三个字符,然后是模式本身,最后打印出模式之后的五个字符。
echo hey there how are you |perl -lne 'print "$1$2$3" if /(.{3})(there)(.{5})/'
ey there how

这也适用于单词而非仅仅是字符。下面将打印出匹配字符串之前的一个单词。
echo hey there how are you |perl -lne 'print $1 if /(\w+) there/'
hey

以下将在模式后打印一个单词:
echo hey there how are you |perl -lne 'print $2 if /(\w+) there (\w+)/'
how

以下将在模式前打印一个单词,然后是实际单词,最后是模式后的一个单词:

echo hey there how are you |perl -lne 'print "$1$2$3" if /(\w+)( there )(\w+)/'
hey there how

0

我个人做法与发布的答案类似。但由于点键像任何键盘键一样可以被轻敲或按住,而且我通常不需要太多上下文(如果我需要更多,我可能会像grep -C那样做行数,但通常像你一样,我不想要前后几行),所以我发现为了输入命令更快,只需轻敲点键即可输入多少个点/多少个字符,如果是几个,则轻敲该键,或者按住它进行更多操作。

例如:echo zzzabczzzz | grep -o '.abc..'

将具有一个点之前和两个点之后的abc模式。(在正则表达式语言中,点匹配任何字符)。其他人也使用点,但用花括号指定重复次数。

如果我想要严格限制在(0或x)个字符和恰好y个字符之间,那么我会使用花括号和-P,就像其他人所做的那样。

有一个设置关于点是否匹配换行符,但如果这是一个问题/兴趣,您可以研究一下。


0

使用 ugrep,您可以通过选项 -o--only-matching)指定上下文为 -ABC,以显示匹配项及其前后的额外字符,使匹配项加上上下文宽度不超过指定的 -ABC。例如:

ugrep -o -C30 pattern testfile.txt

提供:

     1: ... long line with an example pattern to match.  The line could...
     2: ...nother example line with a pattern.

在终端上使用带颜色高亮的相同命令: ugrep --only-matching with context 一行中的多个匹配项将以[+nnn more]的形式显示: enter image description here 或者使用选项-k--column-number)以上下文和列号分别显示每个匹配项: enter image description here 上下文宽度是显示的 Unicode 字符数(UTF-8/16/32),而不仅仅是 ASCII。

-1

你可以使用正则表达式grep来查找,再用第二个grep来进行高亮显示。

echo "some123_string_and_another" | grep -o -P '.{0,3}string.{0,4}' | grep string

23_string_and

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接