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


68

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. 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.

Observe que eu quero usar a linha de comando da maneira normal, então não quero desativar as cores lá - 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 espero que exista 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 iniciado, arquivo digitado
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 limpo doc-src test.ini
spl62 lepl: exit
Script feito, o arquivo é datilografado
spl62 tmp: cat -v datilografado
Script iniciado em Qui 09 Jun 2011 09:47:27 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 ^ M] 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 realizado em Qui 09 Jun 2011 09:47:29 CLT
spl62 tmp: col -b <tipo de letra 
Script iniciado em Qui 09 Jun 2011 09:47:27 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 realizado em Qui 09 Jun 2011 09:47:29 CLT

Respostas:


57

O script a seguir deve filtrar todas as seqüências de controle ANSI / VT100 / xterm para (com base em ctlseqs ). Minimamente testado, por favor relate qualquer sub ou super correspondência.

#!/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 seqüências malformadas. Não é para isso que serve esse script.
  • Argumentos de cadeia de linhas múltiplas para DCS / PM / APC / OSC não são suportados.
  • Os 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 caracteres de controle não ASCII (isso irá alterar 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;
}

graças a ambas as respostas. eu senti que deveria fazer algo como uma boa resposta, embora ambos dêem regexps, o que eu queria evitar. escolheu este, pois fornece uma referência para o formato.
Andrew Cooke

@andrew: Meu regexp é flexível o suficiente para que eu espere que ele funcione com praticamente qualquer terminal já existente e provavelmente com qualquer terminal existente amanhã. Eu não testei muito, então pode haver erros, mas a abordagem é sólida, pois as seqüências de controle seguem alguns padrões gerais.
Gilles 'SO- stop be evil'

forneça como usar esse script. requer entrada de tubo? ou argumentos posicionais?
Trevor Boyd Smith

@TrevorBoydSmith Ambos funcionarão para entrada, e a saída está sempre na saída padrão, como utilitários de texto típicos.
Gilles 'SO- stop be evil'

Isso manipula caracteres multibyte como ☺ (\ xe2 \ x98 \ xba). A cláusula [\ x80- \ x9f] retira o byte do meio.
91118 Jeffrey

31

A atualização da resposta de Gilles para também remover retornos de carro e apagar backspace de caracteres anteriores, que eram importantes para mim para 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;
}

+1 Eu já estava digitando uma postagem com a mesma pergunta do OP quando apreciei esta mensagem com seu script e o de @Gilles. 1 para vocês dois
miracle173

10

Eu usaria sedneste caso.

Faz:

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

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

\x1bcoincidir com o escape anterior ao código de cores \[corresponde ao primeiro colchete aberto .\{1,5\}corresponde a 1 a 5 de qualquer caractere único. Tenha que \usar o aparelho para impedir que a concha os destrua. múltimo caractere em regex - geralmente segue o código de cores. //string vazia para o que substituir tudo. gcombine-o várias vezes por linha.


3
Esse regex retira muito ( foo\e[1m(1m = {torna-se em foo = {vez de foo(m = {), substituindo .por [0-9;]é mais preciso.
Lekensteyn

Substitua .\{1,5\}com [^m]\{1,5\}para isso - mas também notar que esta, mesmo assim, ainda só remove códigos "gráficos rendition" (aqueles que terminam em um m) - basicamente, cor, reverso, negrito e itálico estilos (quando aplicável).
Hannu

Isso não remove \x1b(B(incluído no ferrugens saída de cores)
ideasman42

11
Por que é \x1be não \033?
Atripes

Pode ser em \u001bvez de\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"'

=> como usar:

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

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


3

Resolvi o problema executando scriptreplayuma tela e despejando o buffer de rolagem em um arquivo.

O script esperado a seguir faz isso por você.

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

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

O comando para gerar o arquivo de tempo gerou 100% de uso da CPU por alguns minutos e, após o término, meu uso de memória foi 100% e o comando em execução resultou em "bifurcação: não é possível alocar memória". E realmente não funcionou como esperado.
Barreks2x 27/05

Existe uma maneira muito mais fácil de gerar o arquivo de temporização. Os campos são " delay blocksize", então não há razão para não apenas " 0 <entirefile>" fazê-lo e despejar tudo sem demora. Você pode fazer isso assumindo o tamanho do script menos a primeira linha ( tail -n +2 typescript|wc -c) e criando o arquivo de temporização com echo "0 "`tail -n +2 typescript|wc -c` > timing. Isso será basicamente instantâneo e scriptreplayreproduzirá o script inteiro na velocidade mais rápida possível.
FeRD

1

Encontrei essa pergunta enquanto procurava a solução para o mesmo problema. Um pouco mais cavando e encontrei esse script no Live Journal neste link. Eu trabalhei perfeitamente para mim. Também é uma excelente descrição sobre esse problema e como a solução funciona. Definitivamente vale a pena ler. 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

Eu preferiria usar ferramentas especializadas para converter a saída de script em texto sem formatação, que é constantemente suportado e bem testado, em vez da regexp personalizada. Então, isso funcionou para mim:

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

O comando script captura no arquivo datilografado ansi2txt - converte o código ansi com escapes como códigos de cores, backspaces etc. em texto normal, no entanto, descobri que ainda restam alguns escapes. col -bp - removeu-os completamente.

Eu testei isso no disco mais recente do Ubuntu e funciona.


1

Há um ansi2txtcomando no colorized-logspacote no Ubuntu. Ele remove os códigos de cores ANSI, mas não lida com coisas como barras de progresso produzidas pela emissão ^Hou ^Mcaracteres para substituir o texto no local. col -bpode lidar com isso , para obter melhores resultados, você pode combinar os dois

cat typescript | ansi2txt | col -b

0

Eu descobri que apenas o uso catera tudo o que eu precisava para ver a saída do scriptterminal. Isto não ajuda ao redirecionar a saída para outro arquivo, mas faz o resultado legível, ao contrário cat -v, col -bou um editor de texto.

Para eliminar cores ou salvar os resultados em um arquivo, copie e cole manualmente a saída catem um editor de texto ou em outro catcomando, ou seja:

cat > endResult << END
<paste_copied_text_here>
END

11
sua scriptexecução incluiu saída com códigos de cores anexados, como no caso do OP?
Jeff Schaller

Usar catapresenta as cores originais, que podem ser removidas copiando e colando manualmente. O OP usou cat -ve col -b, os quais apresentam códigos em vez de um resultado final formatado corretamente. Eu editei minha resposta.
Roger Dueck

-2

Seguindo 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. Isso também remove linhas em branco e linhas que começam com uma guia, embora isso funcione para o que estou fazendo. Talvez haja uma maneira de corresponder a qualquer caractere de controle, exceto \ n \ m \ t.

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

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

Isso se parece com uma resposta postada acima, mas não funciona corretamente porque, após a execução do comando, alguns caracteres indesejados já são adicionados à linha de comando como se o usuário os tivesse digitado.


11
Não há "última resposta", pois as respostas podem e mudam de ordem. Você deve usar o botão "compartilhar" abaixo da resposta que deseja referenciar e incluir isso como um link na sua resposta. Supondo que sua resposta seja suficiente para ser mais do que um comentário, é claro. No momento, não consigo identificar qual das várias respostas você está referenciando.
roaima

11
“Poderíamos talvez fazer ...” Sim, poderíamos fazer isso - mas excluiria todas as linhas que começam com um caractere de controle . Na saída de, por exemplo, ls --color(como mostrado na pergunta), sua solução excluirá quase todas as linhas que contêm informações. Não é bom. Mas obrigado por deixar de fora o uso inútil de cat. :-) ⁠
G-Man

Existe uma maneira de criar uma classe de caracteres que é: iscntrl: mas não: isspace :? Talvez alguns sintaxe como ^ [[: iscntrl:] - [: isspace]]
snaran

-4

tr - traduzir ou excluir caracteres

cat typescript | tr -d [[:cntrl:]]

Bem-vindo ao Unix Stackexchange! Ao dar uma resposta, é preferível dar uma explicação sobre POR QUE sua resposta é essa.
Stephen Rauch


3
Na verdade, isso não funcionará corretamente, pois não removerá um, 01;34mpor exemplo, e removerá o final da linha newline (\n).
19417 sorontar
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.