如何截取grep或ack返回的长匹配行

125

我想在经常有非常长的行的HTML文件上运行ack或grep。 我不想看到反复换行的非常长的行。 但是,我确实希望只看到包含与正则表达式匹配的字符串周围部分的长行。如何使用Unix工具的任何组合来实现这一点?


2
ack 是什么?它是当你不喜欢某些东西时使用的命令吗?类似于 ack file_with_long_lines | grep pattern 这样的东西? :-) - Alok Singhal
8
@Alok 提到的 ack(在 Debian 上称为 ack-grep)是一种功能强大的 grep 工具。它还有一个 --thpppt 选项(不是开玩笑的)。更多信息请参考 http://betterthangrep.com/。 - ZoogieZork
谢谢。我今天学到了一些东西。 - Alok Singhal
1
虽然 --thpppt 功能有些争议,但其主要优点似乎在于您可以直接使用 Perl 正则表达式,而不是一些疯狂的 [[:space:]] 和像 {[ 等字符,这些字符会随着 -e-E 开关的变化而改变含义,这种情况很难记住。 - Evgeni Sergeev
类似问题:https://unix.stackexchange.com/q/163726 和 https://dev59.com/nWsz5IYBdhLWcg3wHUS0 - sondra.kinsey
我使用grep --color=always | less -S -R。然后输入-R来展开/折叠行。 - Jérôme Pouiller
11个回答

123
你可以使用grep选项-oE,可能需要将模式更改为".{0,10}<原始模式>.{0,10}",以便在其周围看到一些上下文信息:

       -o, --only-matching
              仅显示与PATTERN匹配的文本行中匹配部分。
-E, --extended-regexp 将模式解释为扩展正则表达式(即强制grep作为egrep运行)。
例如(来自@Renaud的评论):
grep -oE ".{0,10}mysearchstring.{0,10}" myfile.txt

另外,您可以尝试使用-c

       -c,--count
              抑制正常输出;而是为每个输入文件打印匹配行的计数。
              使用 -v,--invert-match 选项(见下文)时,计算不匹配的行。

52
一个例子:grep -oE ".{0,20}mysearchstring.{0,20}" myfile - Renaud
17
请将答案更改为添加-E选项,如@Renaud所示(扩展模式选项),否则将无法使用提议的扩展上下文模式。 - kriss
1
不是很必要,但这里有一个例子: $ echo "eeeeeeeeeeeeeeeeeeeeqqqqqqqqqqqqqqqqqqqqMYSTRINGwwwwwwwwwwwwwwwwwwwwrrrrrrrrrrrrrrrrrrrrr" > fileonelongline.txt && grep -oE ".{0,20}MYSTRING.{0,20}" ./fileonelongline.txt 打印出 qqqqqqqqqqqqqqqqqqqqMYSTRINGwwwwwwwwwwwwwwwwwwww - Ulises Layera
1
这个功能很好用;但是需要注意的缺点是,当使用例如 oE ".{0,20}mysearchstring.{0,20}" 时,你会失去内部“原始”字符串与上下文的高亮显示,因为整个内容都成为了搜索模式。希望能找到一种方法,在搜索结果周围保留一些非高亮的上下文,以便更轻松地进行视觉扫描和结果解释。 - Aaron Wallentine
3
哦,这里有一个解决方案,可以解决使用“-oE“. {0,x} foo。{0,x}“方法(其中x是上下文字符数)引起的突出显示问题 - 在末尾添加| grep foo。适用于ack或grep解决方案。更多解决方案也在这里:https://unix.stackexchange.com/questions/163726/limit-grep-context-to-n-characters-on-line - Aaron Wallentine

56

通过cut命令处理你的结果。我还考虑添加一个--cut开关,这样你就可以使用--cut=80来获取只有80列的结果。


8
如果匹配的部分不在前80个字符中,该怎么办? - Ether
3
我附加了 | cut=c1-120 到 grep 命令中,对我而言有效(尽管不知道如何在匹配的文本周围进行截取)。 - Jake Rayson
35
对我而言,“| cut=c1-120”无法生效,我需要使用“|cut -c1-120”。 - Ken Cochrane
2
我认为@edib在语法上是准确的 | cut -c 1-100 https://dev59.com/ZnI-5IYBdhLWcg3wCz7y#48954102 - CrandellWS
2
@AndyLester:关于使用$COLUMNS--no-wrap选项怎么样? - naught101
显示剩余2条评论

28

您可以使用less作为ack的分页器,并缩短长行:ack --pager="less -S" 这将保留长行但仅在一行上显示,而不是换行。要查看行的更多内容,请在less中使用箭头键向左/右滚动。

我已经设置了以下别名来执行此操作:

alias ick='ack -i --pager="less -R -S"' 

2
请注意,如果您希望始终使用它,可以将 --pager 命令放入您的 ~/.ackrc 文件中。 - Andy Lester
这听起来是迄今为止解决我很烦恼的问题的最佳方案。但愿我知道如何使用 ack - Brian Peterson
@BrianPeterson ack 在大多数情况下与 grep 类似,只是更简单。 - Aaron Wallentine

17

grep -oE ".{0,10}error.{0,10}" mylogfile.txt

在无法使用-E的不寻常情况下,请改用小写字母-e

说明: 命令示例说明


3
请勿在Z shell中使用反斜杠 - grep -oE ".{0,10}error.{0,10}" mylogfile.txt - 来查找错误。 - Aurelijus Rozenas

15

获取从1到100的字符。

cut -c 1-100

您可能希望基于当前终端来设置范围,例如:
cut -c 1-$(tput cols)

3
我把以下内容放入了我的.bashrc文件中:
grepl() {
    $(which grep) --color=always $@ | less -RS
}

您可以在命令行上使用grepl和任何可用于grep的参数。使用箭头键查看更长行的尾部。使用q退出。
解释:
  • grepl() {:定义一个新函数,在每个(新)bash控制台中都可用。
  • $(which grep):获取grep的完整路径。(Ubuntu为grep定义了一个等效于grep --color=auto的别名。我们不想要该别名,而是原始的grep。)
  • --color=always:对输出进行着色。(来自别名的--color=auto无法工作,因为grep检测到输出被放入管道中,然后不会对其进行着色。)
  • $@:将所有传递给grepl函数的参数放在这里。
  • less:使用less显示行
  • -R:显示颜色
  • S:不要换行长行

2

The Silver Searcher (ag)通过--width NUM选项本地支持该功能。它将用[...]替换较长行的其余部分。

示例(在120个字符后截断):

 $ ag --width 120 '@patternfly'
 ...
 1:{"version":3,"file":"react-icons.js","sources":["../../node_modules/@patternfly/ [...]

在ack3中,计划有一个类似的功能,但目前尚未实现。

但是在 ag 中,宽度是从第一个字符“开始”的,所以当字符串位于非常长的行中间时,这种方法并不完全适用。 - Paul

2

源自:http://www.topbug.net/blog/2016/08/18/truncate-long-matching-lines-of-grep-a-solution-that-preserves-color/

建议的方法".{0,10}<原始模式>.{0,10}"非常好,但高亮颜色常常混乱。我创建了一个类似的脚本,输出相似且颜色得到保留:

#!/bin/bash

# Usage:
#   grepl PATTERN [FILE]

# how many characters around the searching keyword should be shown?
context_length=10

# What is the length of the control character for the color before and after the
# matching string?
# This is mostly determined by the environmental variable GREP_COLORS.
control_length_before=$(($(echo a | grep --color=always a | cut -d a -f '1' | wc -c)-1))
control_length_after=$(($(echo a | grep --color=always a | cut -d a -f '2' | wc -c)-1))

grep -E --color=always "$1" $2 |
grep --color=none -oE \
    ".{0,$(($control_length_before + $context_length))}$1.{0,$(($control_length_after + $context_length))}"

假设脚本保存为grepl,那么grepl pattern file_with_long_lines应该显示匹配的行,但只显示匹配字符串周围的10个字符。

代码可以运行,但是输出了一些无用的内容,例如:^[[?62;9;c。我没有尝试调试,因为@Jonah Braun的回答已经让我满意了。 - sondra.kinsey

1
这是我做的事情:
function grep () {
  tput rmam;
  command grep "$@";
  tput smam;
}

在我的 .bash_profile 中,我覆盖了 grep 命令,使其在运行之前自动执行 tput rmam ,并在运行之后执行 tput smam ,这样就禁用了换行,然后重新启用了它。

那是一个不错的选择 - 除非实际匹配超出了屏幕... - xeruf

0

ag还可以使用正则表达式技巧,如果您更喜欢的话:

ag --column -o ".{0,20}error.{0,20}"

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