从脚本输出中删除控制字符(包括控制台代码/颜色)


68

我可以使用“脚本”命令在命令行上记录交互式会话。但是,这包括所有控制字符颜色代码。我可以使用“ col -b”删除控制字符(如退格键),但是找不到删除颜色代码的简单方法。

请注意,我想以常规方式使用命令行,因此不想在此处禁用颜色-我只想从脚本输出中删除它们。此外,我知道可以试一试并尝试找到一个正则表达式来解决问题,但是我希望有一个更简单(更可靠的方法-如果开发我的正则表达式时我不知道有什么代码怎么办?)解决方案。

要显示问题:

spl62 tmp:脚本
脚本启动,文件为打字稿
spl62 lepl:ls
add-licence.sed build-example.sh提交测试push-docs.sh
add-licence.sh build.sh删除许可证.sed setup.py
asn build-test.sh delete-licence.sh src
build-doc.sh干净的doc-src test.ini
spl62 lepl:退出
脚本完成,文件为打字稿
spl62 tmp:cat -v打字稿
脚本从CLT 2011年6月9日上午09:47:27开始
spl62 lepl:ls ^ M
^ [[0m ^ [[00madd-licence.sed ^ [[0m ^ [[00; 32mbuild-example.sh ^ [[0m ^ [[00mcommit-test ^ [[0m ^ [[00; 32mpush-docs.sh ^ [[0m ^ M
^ [[[00; 32madd-licence.sh ^ [[0m ^ [[00; 32mbuild.sh ^ [[0m ^ [[00mdelete-licence.sed ^ [[0m ^ [[00msetup.py ^ [[0m ^ M
^ [[01; 34masn ^ [[0m ^ [[00; 32mbuild-test.sh ^ [[0m ^ [[00; 32mdelete-licence.sh ^ [[0m ^ [[01; 34msrc ^ [[0m ^ M
^ [[00; 32mbuild-doc.sh ^ [[0m ^ [[00; 32mclean ^ [[0m ^ [[01; 34mdoc-src ^ [[0m ^ [[00mtest.ini ^ [[0m ^ M
spl62 lepl:退出^ M

脚本于CLT 2011年6月9日上午09:47:29完成
spl62 tmp:col -b <打字稿 
脚本从CLT 2011年6月9日上午09:47:27开始
spl62 lepl:ls
0m00madd-licence.sed0m 00; 32mbuild-example.sh0m 00mcommit-test0m 00; 32mpush-docs.sh0m
00; 32madd-licence.sh0m 00; 32mbuild.sh0m 00mdelete-licence.sed0m 00msetup.py0m
01; 34masn0m 00; 32mbuild-test.sh0m 00; 32mdelete-licence.sh0m 01; 34msrc0m
00; 32mbuild-doc.sh0m 00; 32mclean0m 01; 34mdoc-src0m 00mtest.ini0m
spl62 lepl:退出

脚本于CLT 2011年6月9日上午09:47:29完成

Answers:


57

以下脚本应过滤掉所有ANSI / VT100 / xterm控制序列(基于ctlseqs)。经过最少测试,请报告任何不匹配或过度匹配。

#!/usr/bin/env perl
## uncolor — remove terminal escape sequences such as color changes
while (<>) {
    s/ \e[ #%()*+\-.\/]. |
       \e\[ [ -?]* [@-~] | # CSI ... Cmd
       \e\] .*? (?:\e\\|[\a\x9c]) | # OSC ... (ST|BEL)
       \e[P^_] .*? (?:\e\\|\x9c) | # (DCS|PM|APC) ... ST
       \e. //xg;
    print;
}

已知的问题:

  • 不要抱怨格式错误的序列。这不是该脚本的目的。
  • 不支持DCS / PM / APC / OSC的多行字符串参数。
  • 尽管很少使用,但可以将128–159范围内的字节解析为控制字符。这是一个解析非ASCII控制字符的版本(它将以某些编码(包括UTF-8)处理非ASCII文本)。
#!/usr/bin/env perl
## uncolor — remove terminal escape sequences such as color changes
while (<>) {
    s/ \e[ #%()*+\-.\/]. |
       (?:\e\[|\x9b) [ -?]* [@-~] | # CSI ... Cmd
       (?:\e\]|\x9d) .*? (?:\e\\|[\a\x9c]) | # OSC ... (ST|BEL)
       (?:\e[P^_]|[\x90\x9e\x9f]) .*? (?:\e\\|\x9c) | # (DCS|PM|APC) ... ST
       \e.|[\x80-\x9f] //xg;
    print;
}

多亏了这两个答案。我觉得我应该做一个很好的答案,尽管两者都给出了正则表达式,但我想避免。选择此格式是因为它为格式提供了参考。
安德鲁·库克

@andrew:我的regexp非常灵活,我希望它可以与几乎任何现有的终端兼容,也可以与将来的任何终端兼容。我没有进行太多测试,因此可能会有错误,但是这种方法听起来很合理,因为控制序列遵循一些通用模式。
吉尔斯

请提供如何使用此脚本。需要管道输入吗?还是位置争论?
Trevor Boyd Smith,

@TrevorBoydSmith任何一个都可以用作输入,并且输出始终位于标准输出上,例如典型的文本实用程序。
吉尔斯

这会破坏多字节字符,例如☺(\ xe2 \ x98 \ xba)。[\ x80- \ x9f]子句剥离中间字节。
Jeffrey

31

更新Gilles的答案还可以删除回车符,并对以前的字符进行退格擦除,这对我在Cygwin上生成的打字稿来说都是很重要的:

#!/usr/bin/perl
while (<>) {
    s/ \e[ #%()*+\-.\/]. |
       \r | # Remove extra carriage returns also
       (?:\e\[|\x9b) [ -?]* [@-~] | # CSI ... Cmd
       (?:\e\]|\x9d) .*? (?:\e\\|[\a\x9c]) | # OSC ... (ST|BEL)
       (?:\e[P^_]|[\x90\x9e\x9f]) .*? (?:\e\\|\x9c) | # (DCS|PM|APC) ... ST
       \e.|[\x80-\x9f] //xg;
       1 while s/[^\b][\b]//g;  # remove all non-backspace followed by backspace
    print;
}

+1当我用您的脚本和@Gilles的脚本喜欢此消息时,我已经在键入与OP相同问题的帖子。你们俩都+1
miracle173

10

我会sed在这种情况下使用。

做:

cat -v typescript | sed -e "s/\x1b\[.\{1,5\}m//g"

sed -e“ s / search / replace / g”是标准的东西。正则表达式解释如下:

\x1b匹配Escape之前的颜色代码 \[匹配第一个开括号 .\{1,5\}匹配任何单个字符的1到5。必须\使用大括号以防止外壳扭曲它们。 m正则表达式中的最后一个字符-通常尾随颜色代码。 //用空字符串替换所有内容。 g每行多次匹配它。


3
此正则表达式条过多(foo\e[1m(1m = {成为foo = {代替foo(m = {),取代.通过[0-9;]更准确。
Lekensteyn 2013年

更换.\{1,5\}[^m]\{1,5\}的是-还要注意这甚至还仅仅删除“图形绘制的”代码(那些在结束m) -基本色,反转,粗体和斜体样式(如适用)。
汉奴2015年

这不会消除\x1b(B(包括在生锈的颜色输出中)
ideaman42 '16

1
为什么\x1b\033
atripes

它可能\u001b不是\x1b
yunzen

9
cat typescript | perl -pe 's/\e([^\[\]]|\[.*?[a-zA-Z]|\].*?\a)//g' | col -b > typescript-processed

6
# The "sed -r" trick does not work on every Linux, I still dunno why:
DECOLORIZE='eval sed "s,${END}\[[0-9;]*[m|K],,g"'

=>使用方法:

<commands that type colored output> | ${DECOLORIZE}

在以下设备上测试过:-AIX 5.x / 6.1 / 7.1-Linux Mandrake / Mandriva / SLES / Fedora-SunOS


3

我通过scriptreplay在屏幕上运行并将回滚缓冲区转储到文件中解决了该问题。

下面的Expect脚本会为您执行此操作。

已针对最多250.000行的日志文件进行了测试。在工作目录中,您需要脚本脚本,一个名为“ time”的文件,其中包含10.000.000倍“ 1 10”行的内容以及脚本。我需要您的脚本文件的名称作为命令行参数,例如./name_of_script name_of_scriptlog

#!/usr/bin/expect -f 

set logfile [lindex $argv 0]

if {$logfile == ""} {puts "Usage: ./script_to_readable.exp \$logfile."; exit}

set timestamp [clock format [clock sec] -format %Y-%m-%d,%H:%M:%S]
set pwd [exec pwd]
if {! [file exists ${pwd}/time]} {puts "ERROR: time file not found.\nYou need a file named time with 10.000.000 times the line \"1 10\" in the working directory for this script to work. Please provide it."; exit}
set wc [exec cat ${pwd}/$logfile | wc -l]
set height [ expr "$wc" + "100" ]
system cp $logfile ${logfile}.tmp
system echo $timestamp >> ${logfile}.tmp
set timeout -1
spawn screen -h $height -S $timestamp 
send "scriptreplay -t time -s ${logfile}.tmp 100000 2>/dev/null\r"
expect ${timestamp} 
send "\x01:hardcopy -h readablelog.${timestamp}\r"

send "exit\r"

system sed '/^$/d' readablelog.$timestamp >> readablelog2.$timestamp
system head -n-2 readablelog2.$timestamp >> ${logfile}.readable.$timestamp
system rm -f readablelog.$timestamp readablelog2.$timestamp ${logfile}.tmp

时间文件可以通过以下方式生成

for i in $(seq 1 10000000); do echo "1 10" >> time; done

生成时间文件的命令在几分钟内产生了100%的CPU使用率,完成后我的内存使用率为100%,运行命令导致“ fork:无法分配内存”。而且它并没有真正按预期工作。
barteks2x

生成定时文件的方法要简单得多。这些字段是“ delay blocksize”,因此没有理由不让它“ 0 <entirefile>”而立即转储整个内容。您可以通过减去脚本大小减去第一行(tail -n +2 typescript|wc -c),然后使用来创建计时文件echo "0 "`tail -n +2 typescript|wc -c` > timing。这基本上是即时的,并且scriptreplay将以最快的速度重放整个脚本。
FeRD '18

1

寻找相同问题的解决方案时发现了这个问题。进行了一些进一步的挖掘,并在此链接的Live Journal中找到了此脚本。我为我完美地工作。关于此问题以及该解决方案的工作方式,这也是很好的文章。绝对值得一读。 http://jdimpson.livejournal.com/7040.html

#!/usr/bin/perl -wp

# clean up control characters and other non-text detritus that shows up 
# when you run the "script" command.

BEGIN {
# xterm titlebar escape sequence
$xtermesc = "\x1b\x5d\x30\x3b";

# the occurence of a backspace event (e.g. cntrl H, cntrol W, or cntrl U)
$backspaceevent = "\x1b\\\x5b\x4b"; # note escaping of third character

# ANSI color escape sequence
$ansiesc = qr/\x1b\[[\d;]*?m/;

# technically, this is arrow-right. For some reason, being used against
# very long backspace jobs. I don't fully understand this, as evidenced
# by the fact that is off by one sometimes.
$bizarrebs = qr/\x1b\[C/;

# used as part of the xterm titlebar mechanism, or when
# a bell sounds, which might happen when you backspace too much.
$bell = "\x07"; # could use \a

$cr = "\x0d"; # could use \r

$backspace = "\x08"; # could use \b
}

s/$xtermesc.+?$bell//g;
s/[$cr$bell]//g;
s/${backspaceevent}//g;
s/$ansiesc//g;
while (s/(.)(?=$backspace)//) { s/$backspace//; } # frickin' sweet 
# For every ^H delete the character immediately left of it, then delete the ^H.
# Perl's RE's aren't R, so I wonder if I could do this in one expression.
while (s/(..)(?=$bizarrebs)//) { s/$bizarrebs//; }

1

我宁愿使用专门的工具将脚本输出转换为纯文本,而纯文本则比自定义regexp受到持续支持和良好测试。所以这对我有用:

$ cat typescript | ansi2txt | col -bp > typescript.txt.bp    
$ cat -v typescript.txt.bp

脚本命令捕获到打字稿文件ansi2txt中-将带有转义符(如颜色代码,退格键等)的ansi代码转换为常规文本,但是我发现夫妇转义符仍然保留。col -bp-完全删除它们。

我已经在最新的Ubuntu迪斯科舞厅上对此进行了测试,并且可以正常工作。


1

Ubuntu上ansi2txtcolorized-logs软件包中有一个命令。它可以很好地删除ANSI颜色代码,但不能处理诸如通过发光产生的进度条^H^M字符覆盖就位文本的事情。 col -b可以处理这些问题,因此为了获得最佳效果,可以将两者结合起来

cat typescript | ansi2txt | col -b

0

我发现仅使用cat即可查看script终端中的输出。将输出重定向到另一个文件时,这无济于事,但与,或文本编辑器不同cat -v,它确实使结果可读col -b

要消除颜色或将结果保存到文件中,请手动将输出复制并粘贴cat到文本编辑器或另一个cat命令中,即:

cat > endResult << END
<paste_copied_text_here>
END

1
您的script运行中是否包含如OP那样带有颜色代码的输出?
杰夫·谢勒

使用cat表示原始颜色,可以通过手动复制和粘贴将其删除。OP使用cat -vcol -b,两者都显示代码,而不是格式正确的最终结果。我已经编辑了答案。
罗杰杜克

-2

跟进使用tr和:cntrl的最后一个答案:我们也许可以做

sed "/^[[:cntrl:]]/d" output.txt

这似乎对我有用,因为vi生成的所有行都以控制字符开头。碰巧也会去除空白行和以制表符开头的行,尽管这对我正在做的事情有用。也许有一种方法可以匹配\ n \ m \ t以外的任何控制字符。

也许我们可以搜索特定的控制字符,并且看起来vi生成的所有垃圾行都以^ [开头。hexdump告诉我第一个字符是1b,所以这似乎也可行

sed "/^\x1b/d" output.txt

这看起来类似于上面发布的答案,但是它无法正常运行,因为在运行命令之后,某些垃圾字符已经被添加到命令行中,就像用户键入了它们一样。


1
没有“最后答案”,因为答案可以并且确实会改变顺序。您应该在要引用的答案下方使用“共享”按钮,并将其作为链接包含在答案中。当然,假设您的回答已不仅仅是评论。目前,我无法确定您要参考的是哪个答案。
roaima

1
“可能我们也许做的......”是的,我们做到这一点-但它会删除每一行开始一个控制字符。例如,在输出ls --color(如问题所示)中,您的解决方案将删除几乎所有包含信息的行。不好。但是感谢省略了对的无用使用cat。:-)⁠
G-人

有没有一种方法可以创建一个字符类:iscntrl:而不是:isspace :?也许有些语法,例如^ [[:iscntrl:]-[:isspace]]
snaran

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.