tr 命令无法处理从 grep 管道传输的颜色输出

tr 命令无法处理从 grep 管道传输的颜色输出

我运行的是 macOS 12.3.1

我在 .zshrc 中添加了几行,即。

export GREP_OPTIONS='--color=always'
export GREP_COLOR='1;35;40'

之后,当我通过管道将 grep 输出传输到 tr 时,它返回相同的行数,但所有行都是空白的

例如:

grep ^.....$ /usr/share/dict/words | tr "[:lower:]" "[:upper:]"

返回 10230 个空行。

这是预期的吗?

答案1

要输出颜色匹配,grep请在匹配之前和之后写入着色转义序列。

这些是指示终端更改其背景和/或前景色的指令。

重要的是要认识到它与文本一起出现在输出中。您看不到它是因为您的终端不会将它们显示为图形符号,而是将其理解为特殊指令。

转义序列以 ESC 字符(0x1b 字节(八进制中的 033),ASCII 又名\e^[)开头,后面跟着一些字符,这些字符本身不必是控制字符。

您可以通过将输出管道传输到以下内容来显示这些字符:

$ echo + | grep --color=always . | sed -n l
\033[01;31m\033[K+\033[m\033[K$
$ echo + | grep --color=always . | od -An -vtc -tx1 -to1
 033   [   0   1   ;   3   1   m 033   [   K   + 033   [   m 033
  1b  5b  30  31  3b  33  31  6d  1b  5b  4b  2b  1b  5b  6d  1b
 033 133 060 061 073 063 061 155 033 133 113 053 033 133 155 033
   [   K  \n
  5b  4b  0a
 133 113 012

(这里还包括各个字节的十六进制和八进制值)

或者(尽管不标准且不明确):

$ echo + | grep --color=always . | cat -A
^[[01;31m^[[K+^[[m^[[K$

您可以在输出中看到grep,有一个\e[01;31\e[K比赛前和\e[m\e[K比赛后。

给定终端识别哪些转义序列以及如何随终端而变化。例如,对于 xterm,请参见那里的规格。如今,上述内容相当普遍。

对于以 开头\e[和结尾的数字m,终端将每个;用 - 分隔的数字理解为不同的渲染属性,以应用于从现在开始写入的文本。例如1对于粗体,31将前景色设置为红色。

\e[K是告诉终端清除屏幕从光标到行尾的转义序列。

所以终端实际上​​看到:

<bold-fg_red><clear-to-eol>+<reset-all-attributes><clear-to-eol>

但所tr看到的只是那些 ESC、[...m以及其他需要音译的内容。

特别是在这里,它将音译mM,并且改变颜色属性的转义序列将一起变成其他东西。

要了解转义序列及其用途,除了查看终端文档(例如https://www.invisible-island.net/xterm/ctlseqs/ctlseqs.html上面提到的 xterm),有时很难找到或不存在,您还可以查看数据库terminfo,该数据库记录了一些常见操作的一些终端识别的转义序列。

您可以使用以下命令手动查询终端的数据库(由$TERM环境变量标识)infocmp

$ infocmp -xL1 | grep M,
        delete_line=\E[M,
        key_enter=\EOM,
        key_mouse=\E[M,
        parm_delete_line=\E[%p1%dM,
        scroll_reverse=\EM,

以及这些行动的详细信息(能力),您可以查看terminfo(5)手册页(man 5 terminfo)。

\e[M( delete_line) 删除一行,\e[<decimal>M( parm_delete_line) 删除<decimal>行。因此,一旦音译为大写,您的着色序列就变成了行删除序列。

您通常不想对彩色输出进行后处理,因为这些输出仅适用于终端。这就是为什么大多数支持着色的命令在其输出未到达终端时会禁用它。

对于 GNU grep,正如您已经发现的那样,您需要--color=auto(或grep --color) 来获得该行为。

现在,如果您仍然想查看颜色,则需要将颜色移动到管道中的最后一个命令,即输出到终端的命令:

<file tr '[:lower:]' '[:upper:]' | grep  -xE --colour=auto '.{5}'

此处使用--colour=auto,以便如果包含该命令的脚本对其输出进行重定向/后处理,则着色将被禁用。

在这里,由于正则表达式匹配整行(-x上面的选项,这避免了在您的方法中必须使用^and $like ),您不妨在之前将前景色切换为红色,并在之后清除属性:

if [ -t 1 ]; then
  tput setaf 1 # set ANSI foreground colour
  tput bold
fi
grep...
if [ -t 1 ]; then
  tput sgr0 # turn off all attributes
fi

这里用于tput查询terminfo适合您的终端的正确序列,但由于大多数终端都这样做,因此请对grep序列进行类似和硬编码:

[ -t 1 ] && printf '\33[1;31m'
grep...
[ -t 1 ] && printf '\e[m'

用于[ -t 1 ]检查 stdout(文件描述符 1)是否是终端。

答案2

我通过将其更改 GREP_OPTIONS为来--color=always修复它--color=auto

我认为 tr 和颜色编码文本不能很好地结合在一起

相关内容