颜色正则表达式匹配 - 不丢失未匹配项

22

使用命令 grep --color=always 可以为正则表达式匹配的内容提供漂亮的彩色高亮。

然而,grep 命令只返回至少有一个匹配的行。我想要的是一种方法,可以突出显示正则表达式匹配的内容,而不改变所有其他输入,并保留没有任何匹配的行。

我尝试了在 sed 中实现颜色显示,并阅读了 grep 的文档,但似乎无法得到我想要的结果。

如果我的描述不够清楚,我希望:

输入:

  • fred
  • ted
  • red
  • lead

正则表达式:

  • ".*red"

输出:

  • fred ( in red )
  • ted
  • red ( in red )
  • lead

这样我就可以执行以下操作:

list_stuff | color_grep "make_this_stand_out_but_dont_hide_the_rest"

编辑:

我已经找到了一个解决方案,虽然不太美观,但它起作用:

感谢: http://www.pixelbeat.org/docs/terminal_colours/

特别是这个脚本(我进行了修改/简化): http://www.pixelbeat.org/talks/iitui/sedgrep

function sedgrep ()
{
    C_PATT=`echo -e '\033[33;01m'`
    C_NORM=`echo -e '\033[m'`

    sed -s "s/$1/${C_PATT}&${C_NORM}/gi"
}

仍在寻找更简单的方法来完成这个任务!

7个回答

22
最简单的解决方案是使用egrep --color=always 'text|^',这将匹配所有行开头但只着色所需文本。

3
这个对我没用,但这个可以:egrep --color=always 'text|$'。顺便说一下,我正在使用zsh。 - Tony
从ZSH 5.8开始,egrep --color=always 'text|^' 对我来说是有效的。 - karatedog

9
这是我用来着色输出的脚本。
我想我是在某个博客或bash/sed教程上找到了这个想法/片段——现在找不到它了,那是很久以前的事了。
#!/bin/bash

red=$(tput bold;tput setaf 1)            
green=$(tput setaf 2)                    
yellow=$(tput bold;tput setaf 3)         
fawn=$(tput setaf 3)
blue=$(tput bold;tput setaf 4)           
purple=$(tput setaf 5)
pink=$(tput bold;tput setaf 5)           
cyan=$(tput bold;tput setaf 6)           
gray=$(tput setaf 7)                     
white=$(tput bold;tput setaf 7)          
normal=$(tput sgr0)                      

sep=`echo -e '\001'` # use \001 as a separator instead of '/'

while [ -n "$1" ] ; do
  color=${!1}
  pattern="$2"
  shift 2

  rules="$rules;s$sep\($pattern\)$sep$color\1$normal${sep}g"
done

#stdbuf -o0 -i0 sed -u -e "$rules"
sed -u -e "$rules"

使用方法:

./colorize.sh color1 pattern1 color2 pattern2 ...

e.g.

dmesg | colorize.sh red '.*Hardware Error.*' red 'CPU[0-9]*: Core temperature above threshold' \
green 'wlan.: authenticated.*' yellow 'wlan.: deauthenticated.*'

虽然它不能很好地处理重叠的图案,但我仍然发现它非常有用。

希望这对你有所帮助。


我不得不替换 sep='/',否则会显示错误,但之后它运行得非常好!谢谢! - Kia Kaha

3
这个小函数在我的ZShell中运行良好:
function color_grep {
    sed s/$1/$fg[yellow]$1$terminfo[sgr0]/g
}

(Needs

autoload colors zsh/terminfo

也许你可以做类似的事情?

编辑:抱歉,这不能用正则表达式解决。您需要稍微调整一下...


我正在使用Bash,但那似乎是与我发布的解决方案几乎相同类型的解决方案。谢谢! - mmocny

1

你现在的做法可能已经是尽可能简洁的了,除非你自己编写grep工具。如果你不一定关心输出的顺序,这里还有另一种我能想到的方法:

function colormatch ()
{
    tee - | grep --color=always $1 | sort | uniq
}

使用sed不如效率高(会创建更多的进程,并且需要使用tee命令输出),因此我建议您继续使用sed解决方案。


很好。你的解决方案有一个更容易记住的语法,对于那些没有.bashrc文件的时候来说非常方便 :) - mmocny

1
你可以使用 -C<num> 选项来进行 grep,它会显示与匹配相关的 <num> 行上下文。只需确保 <num> 至少与文件中的行数一样大即可。

哦,巧妙地滥用上下文选项,我喜欢它 :-) - Jay
1
实际上,我尝试过这个方法,但它有三个问题: 第一,如果没有匹配项,它将不会返回任何内容。 第二,在<num>非常大的情况下,速度会很慢。 第三(最大的问题),结果会随着每个“匹配项”被打印出来,因此如果我在管道上运行此命令,输出会以块的形式呈现。 - mmocny
然而,这是一种非常聪明的快速解决方法,特别是对于小结果,如果你知道有一个命中的话 :) 我在不使用自己的电脑时经常这样做。 - mmocny

0

0

我最近做了一个类似过滤器的东西。我用它来给带有多个文件的尾部“标头”着色,像这样:

tail -f access.log error.log foo.log | logcol.sh

“标头”看起来像这样:

==> access.log <==

我因为不同日志文件之间快速切换而感到困惑,所以这个logcol.sh有所帮助。==>是针对特定使用硬编码的,但也可以作为参数。

#!/bin/sh
while read line
do
  if test `expr "$line" : "==>.*"`  -eq 0 ;
  then
    printf '\033[0m%s\n' "$line"
  else
    printf '\033[0;31m%s\n' "$line"
  fi
done

可能不是最优雅的,但我认为它相当易读。 希望我没有打错字;-) 祝好, 罗布


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