从输出中删除颜色

239

我有一些脚本会输出带着颜色的内容,我需要去除其中的ANSI码。

#!/bin/bash

exec > >(tee log)   # redirect the output to a file but keep it on stdout
exec 2>&1

./somescript

输出结果(在日志文件中):

java (pid  12321) is running...@[60G[@[0;32m  OK  @[0;39m]

我不知道如何在这里放置ESC字符,所以我用@代替它。

我将脚本改为:

#!/bin/bash

exec > >(tee log)   # redirect the output to a file but keep it on stdout
exec 2>&1

./somescript | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g"

但是现在它给了我(在日志文件中):

java (pid  12321) is running...@[60G[  OK  ]

我该如何删除这个'@[60G'符号?

也许有一种方法可以完全禁用整个脚本的着色?


2
对于node/npm,您可以使用strip-ansi:https://github.com/chalk/strip-ansi。 - Joshua Pinter
这个问题之前已经有人问过并得到了答案:https://dev59.com/rmw15IYBdhLWcg3wkMjz#6534712 - mike
22个回答

249

根据维基百科的说法, 你正在使用的sed命令中的[m|K]是专门设计用来处理m(颜色命令)和K(“擦除行的一部分”命令)。你的脚本试图将绝对光标位置设置为60 (^[[60G),以获取一行中的所有OK,但你的sed命令没有涵盖到。

(正确的做法应该是(m|K)[mK],因为你不是在匹配管道字符。但这并不重要。)

如果你将命令中的最后一个匹配改为[mGK](m|G|K),你就应该能够捕获那个额外的控制序列。

./somescript | sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2};?)?)?[mGK]//g"

47
BSD/OSX用户:通常我们没有sed命令的-r选项。brew install gnu-sed会安装一个具有能力的版本。请使用gsed来运行该命令。 - Nicolai S
2
据我所知,这里的区别在于超过16种颜色设置(如setaf支持的)需要比仅有两个更多的参数;我的正则表达式只支持两个。将第一个?替换为*应该会有所帮助。处理sgr0是可能的,但根据搜索结果,它很可能超出了这个基于hacky正则表达式的答案的范围。 - Jeff Bowman
8
由于存在第三个值(例如 [38;5;45m),因此此方法无法可靠地工作。这个备选答案可以解决问题:https://unix.stackexchange.com/a/55547/168277。 - davemyron
4
将以下内容添加到你的bashrc中,作为别名decolorize来实现颜色去除功能:alias decolorize='sed -r "s/\\x1B\\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]//g"'。这个小工具可以通过command | decolorize或者decolorize file.log两种方式使用。 - Neinstein
7
更正确的形式是 sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,3})*)?[mGK]//g",因为可能有更多的术语,并且它们也可以是3位数。例如 \e[38;5;123m 或者甚至是 \e[38;5;123;48;5;246m - FERcsI
显示剩余6条评论

86

在我看来,大多数答案都试图过于限制转义代码中的内容。结果,它们错过了常见的代码,如[38;5;60m(256色模式下的前景ANSI颜色60)。

它们还需要-r选项,该选项启用GNU扩展。这些不是必需的;它们只是使正则表达式更易读。

以下是一个更简单的答案,处理256色转义并适用于非GNU sed系统:

./somescript | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g'

这将捕获任何以[开头,具有任意数量的小数点和分号,并以字母结尾的内容。这应该捕获常见的ANSI转义序列中的任何一个。

为了好玩,这里有一个更大更通用(但最小化测试)的解决方案,适用于所有可能的ANSI转义序列

./somescript | sed 's/\x1B[@A-Z\\\]^_]\|\x1B\[[0-9:;<=>?]*[-!"#$%&'"'"'()*+,.\/]*[][\\@A-Z^_`a-z{|}~]//g'

(如果您遇到@edi9999的SI问题,可以在结尾处添加 | sed "s/\x0f//g";这适用于任何控制字符,通过将0f替换为不需要的字符的十六进制值来实现)

1
这个很好地从 Azure az cli 美化的输出中提取字符串颜色。 - volvox
1
@halfer,当使用sed而没有使用-r选项时,+被视为字面量,而\+则被视为修饰符,这与大多数现代用法相矛盾。 - meustrus
如果您使用gsed而不是sed,这些命令可以在macOS上运行。 - slm
2
这是我首选的答案,但对于我的用例存在一个小问题,我正在处理的输出包含未被捕获的 ^[[m。通过像这样进行修改解决:./somescript | sed 's/\x1B\[[0-9;]*[A-Za-z]//g' - bxm
这个解决方案对我没有用。它留下了一些转义代码,在终端中看不到,但在文本文件中可以看到(由git diff生成)。被接受的答案起作用了。 - KernelDeimos
显示剩余4条评论

69

我在Debian的colorized-logs包中发现了一个名为ansi2txt的工具。该工具可以从标准输入中删除ANSI控制代码。

使用示例:

./somescript | ansi2txt

源代码http://github.com/kilobyte/colorized-logs


7
“colorized-logs”软件包存在于Ubuntu的标准存储库中,使用命令“sudo apt install colorized-logs”即可轻松安装此工具。该工具运行良好,我尚未发现任何问题。这应该是官方答案。 - Hans Deragon
2
同样适用于AUR用户:aur/colorized-logs - draxil
从手册页面:所有 ANSI 代码都会被忽略,包括所有光标定位代码。该软件包删除了所有彩色部分,例如时间戳,这使得它变得相当无用。 - Roman Shishkin
似乎也可以使用pip install --user ansi2txt或者pip install ansi2txt进行安装。 - undefined

49

我在其他答案中都不能得到满意的结果,但是下面的方法对我有效:

somescript | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g"
如果仅仅去掉控制字符“^[”,那么颜色数据的其他部分将会保留,例如"33m"。包括颜色代码和“m”就能解决问题。我对于s/\x1B//g不起作用感到困惑,因为\x1B[31m在echo中确实有效。

7
在OSX(BSD sed)中,使用-E代替-r来表示扩展正则表达式。更多信息可以在这里找到。 - Assambar
我不得不将{1,3}替换为{,3}(否则它仍然会跳过一些控件),感谢您的解决方案! - actionless
9
由于它们可能是用分号分隔的多个数字(用于背景颜色、粗体、斜体等...)。这个命令对我很有效:sed -r "s/[[:cntrl:]]\[([0-9]{1,3};)*[0-9]{1,3}m//g" - saeedgnu
这个(在我测试过的许多中)可以处理使用unbuffer运行的Ansible输出。 - Martin
对于那些想要使用less命令查看包含颜色代码的日志的人,这个命令在我的Ubuntu上有效。cat errors.log | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g" | tee errors-copy.log | less errors-copy.log - SlurpGoose
这对我来说并没有捕获所有的转义代码,不像预期的答案那样。 - SuperSandro2000

45

对于Mac OSX或BSD使用

./somescript | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g'

1
奇怪,这个在 Debian 上运行良好,但其他的却不行。 - Dmitry
这个部分是有效的。但是,如果我在Excel中打开一个文件,仍然会在每行末尾看到特殊字符“?”。 - doudy_05
@doudy_05 试着在 sed 命令后加上 -E 标记来启用扩展正则表达式。 - Alexander Zinchenko
适用于 Raspbian(部分,我可以看到 ^M)和 MacOs Big Sur(完全)。谢谢,节省了我的时间。 - shukshin.ivan
1
这个适用于Busybox(华硕路由器),而其他的不行。 - certainlyakey

33

下面的正则表达式会错过一些ANSI转义代码序列,以及三位数字颜色。示例修复方法请参见regex101.com。

请改用以下正则表达式:

./somescript | sed -r 's/\x1B\[(;?[0-9]{1,3})+[mGK]//g'

我也遇到过这个问题,有时候会出现SI字符。

例如在这个输入中就会出现:echo "$(tput setaf 1)foo$(tput sgr0) bar"

以下是另一种方法,可以去掉SI字符(shift in)(0x0f)

./somescript | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | sed "s/\x0f//g"

2
不确定为什么这个答案得到的赞数这么少。这是唯一一个对我有效的... - m8mble
1
这个程序接近工作状态,但它忽略了三位数的情况和颜色代码序列,例如:U+001B[38;2;128;128;128m。请参见 https://regex101.com/r/Qjtopi/1 上未找到的颜色。我使用的正则表达式可以在 https://regex101.com/r/wYygBw/1 找到。 - SgtPooki

11

在纯Bash中,更简单的函数可用于从文本流中过滤掉常见ANSI代码:

# Strips common ANSI codes from a text stream

shopt -s extglob # Enable Bash Extended Globbing expressions
ansi_filter() {
  local line
  local IFS=
  while read -r line || [[ "$line" ]]; do
    printf '%s\n' "${line//$'\e'[\[(]*([0-9;])[@-n]/}"
  done
}

参见:

  1. linuxjournal.com:扩展通配符
  2. gnu.org:Bash参数扩展

1
这个不起作用。使用 tldr 进行测试。(虽然我使用的是 zsh,所以也可能是因为这个原因。) - HappyFace
实际上,Zsh 不会理解 Bash 的扩展 globing extglob,或者它可能完全不理解字符串替换。 - Léa Gris
我已经启用了zsh的extendedglob...字符串替换也应该是posix吗? - HappyFace
字符串替换不是POSIX标准。您可以使用此处提到的任何使用sed的备用方法,这些方法将与Zsh一起使用。 - Léa Gris
1
这个解决方案的优点是对文本进行了行缓冲。我尝试使用sed,但它会阻塞我的管道。 - Guillermo Prandi
这里的基本思想是进行简单的字符串替换,对我来说效果很好:https://dev59.com/KWYs5IYBdhLWcg3wBvRp#13210909 我有非常具体的代码需要删除(红色、绿色、停止颜色),我有一个丑陋的6-8行代码来删除每个颜色代码。我相信如果你让正则表达式更简单,这可能是一种不错的解决方案。(也许$'\e'部分特别棘手?) - Devin Rhode

10

如果您想自己构建它,源代码位于https://gitlab.com/saalen/ansifilter/。 - mike
1
brew install ansifilter - ijoseph

9
我遇到了类似的问题。我找到的所有解决方法都可以很好地处理颜色代码,但不能删除由"$(tput sgr0)"(重置属性)添加的字符。
以davemyron的评论中的解决方案为例,在下面的示例中,结果字符串的长度为9,而不是6:
#!/usr/bin/env bash

string="$(tput setaf 9)foobar$(tput sgr0)"
string_sed="$( sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" <<< "${string}" )"
echo ${#string_sed}

为了使正则表达式正常工作,必须将其扩展以匹配由sgr0 ("\E(B")添加的序列:
string_sed="$( sed -r "s/\x1B(\[[0-9;]*[JKmsu]|\(B)//g" <<< "${string}" )"

@Jarodiv - 感谢您提供最全面的方法。在此主题上提供的所有答案仅涉及ANSI / VT100控制序列(例如:“\e[31mHello World\e[0m”),但未纠正由TPUT文本格式引起的任何问题(例如:tput smso / tput setaf X / tput rmso / tput sgr0)。因此,在所有'sed'执行之后,日志中仍然存在其他混乱。这是我使用情况的纯解决方案! - faceless

8

我不确定./somescript中包含什么内容,但如果转义序列没有硬编码,您可以设置终端类型以避免它们。

TERM=dumb ./somescript 

例如,如果您尝试执行以下操作:
TERM=dumb tput sgr0 | xxd

你会看到它不会产生任何输出,但是

tput sgr0 | xxd
00000000: 1b28 421b 5b6d                           .(B.[m

对于 xterm-256color,执行以下操作。


1
迄今为止最简单的解决方案/答案! - EdwardTeach
假设您的工具注意终端类型。通常,表现良好的工具已经有一个开关来禁用ANSI序列(通常命名不当,如“--no-color”)。 - sehe
@sehe,这就是为什么我提到了“但如果转义序列没有硬编码...” - Diego Torres Milano
我已经点赞,但在一般情况下这并不容易确定。我猜会有少数工具使用tput,还有一些试图智能化(甚至有时正确)处理终端能力的工具。 - sehe
TERM=dumb 伙计们 - fabrizioM

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