Removendo caracteres de controle (incluindo códigos / cores do console) da saída do script

61

Eu posso usar o comando "script" para gravar uma sessão interativa na linha de comando. No entanto, isso inclui todos os caracteres de controle e códigos de cores. Eu posso remover caracteres de controle (como backspace) com "col -b", mas não consigo encontrar uma maneira simples de remover os códigos de cores.

Note que eu quero usar a linha de comando normalmente, então não desabilite as cores - eu só quero removê-las da saída do script. Além disso, eu sei que posso brincar e tentar encontrar um regexp para consertar as coisas, mas eu espero que haja uma solução mais simples (e mais confiável - e se houver um código que eu não conheço quando desenvolvo o regexp?).

Para mostrar o problema:

spl62 tmp: script
Script started, file is typescript
spl62 lepl: ls
add-licence.sed  build-example.sh  commit-test         push-docs.sh
add-licence.sh   build.sh          delete-licence.sed  setup.py
asn              build-test.sh     delete-licence.sh   src
build-doc.sh     clean             doc-src             test.ini
spl62 lepl: exit
Script done, file is typescript
spl62 tmp: cat -v typescript
Script started on Thu 09 Jun 2011 09:47:27 AM CLT
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: exit^M

Script done on Thu 09 Jun 2011 09:47:29 AM CLT
spl62 tmp: col -b < typescript 
Script started on Thu 09 Jun 2011 09:47:27 AM CLT
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: exit

Script done on Thu 09 Jun 2011 09:47:29 AM CLT
    
por andrew cooke 09.06.2011 / 15:51

9 respostas

52

O script a seguir deve filtrar todas as sequências de controle ANSI / VT100 / xterm para (com base em ctlseqs ). Minimamente testado, por favor relate qualquer sub ou over-match.

#!/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;
}

Problemas conhecidos:

  • Não reclama de sequências malformadas. Não é para isso que este roteiro é.
  • Argumentos de cadeia de várias linhas para DCS / PM / APC / OSC não são suportados.
  • Bytes no intervalo de 128 a 159 podem ser analisados como caracteres de controle, embora isso raramente seja usado. Aqui está uma versão que analisa os caracteres de controle não-ASCII (isso irá manchar o texto não-ASCII em algumas codificações, incluindo UTF-8).
#!/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;
}
    
por 09.06.2011 / 22:43
28

Atualizando a resposta de Gilles para também remover retornos de carro e fazer o backspace de apagar caracteres anteriores, ambos importantes para mim em um texto datilografado gerado no 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;
}
    
por 19.08.2011 / 00:13
8

Eu usaria sed neste caso.

faça:

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

sed -e "s / search / replace / g" é uma coisa padrão. o regex é explicado como abaixo:

\x1b corresponde ao Escape anterior ao código de cor \[ corresponde ao primeiro colchete aberto .\{1,5\} corresponde de 1 a 5 de qualquer caractere único. Tem que \ as chaves para manter o shell de mangling-los. m último caractere em regex - geralmente segue o código de cores. // string vazia para o que substituir tudo. g corresponde várias vezes por linha.

    
por 09.06.2011 / 20:41
7
cat typescript | perl -pe 's/\e([^\[\]]|\[.*?[a-zA-Z]|\].*?\a)//g' | col -b > typescript-processed
    
por 27.09.2013 / 02:44
5
# The "sed -r" trick does not work on every Linux, I still dunno why:
DECOLORIZE='eval sed "s,${END}\[[0-9;]*[m|K],,g"'

= > Como usar:

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

testado em: - AIX 5.x / 6.1 / 7.1 - Linux Mandrake / Mandriva / SLES / Fedora - SunOS

    
por 24.09.2014 / 17:28
2

Eu resolvi o problema executando scriptreplay em uma tela e descarregando o buffer de rolagem em um arquivo.

O script a seguir a seguir faz isso para você.

Foi testado para arquivos de log com até 250.000 linhas. No diretório de trabalho, você precisa do seu script, um arquivo chamado "time" com 10.000.000 vezes a linha "1 10" e o script. Eu preciso do nome do seu scriptfile como argumento de linha de comando, como ./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

O arquivo de tempo pode ser gerado por

for i in $(seq 1 10000000); do echo "1 10" >> time; done
    
por 22.01.2015 / 13:37
1

Encontrou esta questão enquanto procurava a solução para o mesmo problema. Um pouco mais de escavação e encontrei este script no Live Journal neste link. Eu trabalhei perfeitamente para mim. Também é muito bom escrever sobre esse problema e como a solução funciona. Definitivamente vale a pena ler. link

#!/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//; }
    
por 18.10.2017 / 02:46
-2

Continuando a última resposta que usa tr e: cntrl: poderíamos talvez fazer

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

Isso parece funcionar para mim porque todas as linhas geradas pelo vi começam com um caractere de controle. Acontece que também tira linhas e linhas em branco que começam com uma tabulação, embora isso funcione para o que estou fazendo. Talvez haja uma maneira de corresponder a qualquer caractere de controle, exceto para \ n \ m \ t.

Talvez possamos procurar o caractere de controle específico e parece que todas as linhas de lixo geradas pelo vi começam com o que parece ser ^ [. hexdump me diz que o primeiro caractere é 1b, então isso parece funcionar também

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

Isso é semelhante a uma resposta postada acima, mas não funciona corretamente porque depois de executar o comando, alguns caracteres inúteis já foram adicionados à linha de comando como se o usuário os tivesse digitado.

    
por 23.06.2017 / 23:01
-4

tr - traduzir ou excluir caracteres

cat typescript | tr -d [[:cntrl:]]
    
por 19.02.2017 / 17:43