Usuwanie znaków kontrolnych (w tym kodów / kolorów konsoli) z danych wyjściowych skryptu


68

Mogę użyć polecenia „skrypt”, aby nagrać sesję interaktywną w wierszu polecenia. Obejmuje to jednak wszystkie znaki kontrolne i kody kolorów. Mogę usunąć znaki kontrolne (np. Backspace) za pomocą „col -b”, ale nie mogę znaleźć prostego sposobu na usunięcie kodów kolorów.

Zauważ, że chcę używać wiersza poleceń w normalny sposób, więc nie chcę tam wyłączać kolorów - chcę tylko usunąć je z wyników skryptu. Wiem też, że mogę się pobawić i spróbować znaleźć wyrażenie regularne, aby to naprawić, ale mam nadzieję, że istnieje prostsze (i bardziej niezawodne - co, jeśli istnieje kod, o którym nie wiem, kiedy opracowuję wyrażenie regularne?).

Aby pokazać problem:

spl62 tmp: skrypt
Skrypt uruchomiony, plik jest maszynopisem
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
Skrypt wykonany, plik jest maszynopisem
spl62 tmp: cat -v maszynopis
Skrypt został uruchomiony w czwartek 09 czerwca 2011 09:47:27 AM CLT
spl62 lepl: ls ^ M
^ [[0m ^ [[00madd-licence.sed ^ [[0m ^ [[00; 32mbuild-example.sh ^ [[0m ^ [[00m test komendowy ^ [[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

Skrypt wykonany w czwartek 09 czerwca 2011 09:47:29 AM CLT
spl62 tmp: col -b <maszynopis 
Skrypt został uruchomiony w czwartek 09 czerwca 2011 09:47:27 AM CLT
spl62 lepl: ls
0m00madd-licence.sed0m 00; 32mbuild-example.sh0m 00m commit-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

Skrypt wykonany w czwartek 09 czerwca 2011 09:47:29 AM CLT

Odpowiedzi:


57

Poniższy skrypt powinien odfiltrować wszystkie sekwencje kontrolne ANSI / VT100 / xterm dla (na podstawie ctlseqs ). Minimalnie przetestowane, zgłoś wszelkie niedopasowania lub przeregulowania.

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

Znane problemy:

  • Nie narzeka na zniekształcone sekwencje. Nie do tego służy ten skrypt.
  • Argumenty zawierające wiele wierszy do DCS / PM / APC / OSC nie są obsługiwane.
  • Bajty z zakresu 128–159 mogą być analizowane jako znaki kontrolne, chociaż jest to rzadko używane. Oto wersja, która analizuje znaki sterujące spoza ASCII (spowoduje to zniekształcenie tekstu spoza ASCII w niektórych kodowaniach, w tym 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;
}

dzięki obu odpowiedziom. czułem, że powinienem zrobić coś jako dobrą odpowiedź, chociaż oba dają wyrażenia regularne, których chciałem uniknąć. wybrałem ten, ponieważ zawiera odniesienie do formatu.
andrew cooke

@andrew: Moje wyrażenie regularne jest na tyle elastyczne, że spodziewam się, że będzie działać z niemal każdym terminalem już istniejącym i prawdopodobnie z każdym terminalem jutrzejszym. Nie testowałem tego zbyt wiele, więc mogą występować błędy, ale podejście jest rozsądne, ponieważ sekwencje kontrolne są zgodne z kilkoma ogólnymi wzorami.
Gilles

proszę podać, jak korzystać z tego skryptu. czy wymaga wprowadzania rur? lub argumenty pozycyjne?
Trevor Boyd Smith

@TrevorBoydSmith Albo będzie działać na dane wejściowe, a dane wyjściowe są zawsze na standardowym wyjściu, podobnie jak typowe narzędzia tekstowe.
Gilles

To zmienia znaki wielobajtowe, takie jak ☺ (\ xe2 \ x98 \ xba). Klauzula [\ x80- \ x9f] usuwa środkowy bajt.
Jeffrey

31

Aktualizacja odpowiedzi Gillesa, aby usunąć również znaki powrotu karetki i wyczyścić poprzednie znaki, które były dla mnie ważne dla maszynopisu wygenerowanego w 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 Już pisałem post z tym samym pytaniem, co OP, gdy lubiłem tę wiadomość w twoim skrypcie i @Gilles. +1 dla was
obojga

10

Chciałbym użyć sedw tym przypadku.

zrobić:

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

sed -e "s / search / replace / g" jest standardową rzeczą. Wyrażenie regularne jest wyjaśnione jak poniżej:

\x1bdopasowanie Escape poprzedzające kod koloru \[odpowiada pierwszemu otwartemu nawiasowi .\{1,5\}odpowiada od 1 do 5 dowolnego pojedynczego znaku. Muszą \kręcone szelki, aby powstrzymać muszlę przed nimi. mostatni znak w wyrażeniu regularnym - zwykle śledzi kod koloru. //pusty ciąg znaków na co zastąpić wszystko. gdopasuj to wiele razy w linii.


3
Ten wyrażenie regularne za dużo usuwa ( foo\e[1m(1m = {staje się foo = {zamiast foo(m = {), zastępowanie .przez [0-9;]jest bardziej dokładne.
Lekensteyn,

Wymienić .\{1,5\}z [^m]\{1,5\}tego - ale również pamiętać, że nawet wtedy jeszcze tylko usuwa „grafika wydania” kody (te, które kończą się m) - w zasadzie kolorów, do tyłu, pogrubienie i kursywa Styl (o ile dotyczy).
Hannu,

To nie usuwa \x1b(B(zawarte w wydruku koloru rdzy)
ideasman42

1
Dlaczego tak jest, \x1ba nie \033?
atripes

Może to być \u001bzamiast\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"'

=> jak korzystać:

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

testowane na: - AIX 5.x / 6.1 / 7.1 - Linux Mandrake / Mandriva / SLES / Fedora - SunOS


3

Rozwiązałem problem, uruchamiając scriptreplayekran i zrzucając bufor przewijania do pliku.

Poniższy skrypt oczekiwań robi to za Ciebie.

Został przetestowany dla plików logów zawierających do 250 000 linii. W katalogu roboczym potrzebujesz swojego dziennika skryptów, pliku o nazwie „czas” z 10.000.000 razy większą niż linia „1 10” oraz skryptu. Potrzebuję nazwę twojego pliku skryptu jako argument wiersza poleceń, jak ./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

Plik czasu może zostać wygenerowany przez

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

Polecenie generowania pliku czasu generowało 100% użycia procesora przez kilka minut, a po jego zakończeniu zużycie pamięci wyniosło 100%, a uruchomienie polecenia spowodowało „rozwidlenie: nie można przydzielić pamięci”. I tak naprawdę to nie działało zgodnie z oczekiwaniami.
barteks2x

Istnieje znacznie łatwiejszy sposób na wygenerowanie pliku czasowego. Pola są „ delay blocksize”, więc nie ma powodu, aby po prostu nie „ 0 <entirefile>” i zrzucić wszystkiego bez zwłoki. Możesz to zrobić, biorąc rozmiar skryptu minus pierwszy wiersz ( tail -n +2 typescript|wc -c) i utwórz plik pomiaru czasu za pomocą echo "0 "`tail -n +2 typescript|wc -c` > timing. Będzie to w zasadzie natychmiastowe i scriptreplayodtworzy cały skrypt z największą możliwą prędkością.
FeRD

1

Znalazłem to pytanie, szukając rozwiązania tego samego problemu. Trochę więcej kopie i znalazłem ten skrypt w Live Journal pod tym linkiem. Pracowałem dla siebie idealnie. Jest to również bardzo dobry opis tego problemu i działania rozwiązania. Zdecydowanie warte przeczytania. 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

Wolałbym używać specjalistycznych narzędzi do konwersji danych wyjściowych skryptu na zwykły tekst, który jest stale obsługiwany i dobrze testowany, zamiast niestandardowych wyrażeń regularnych. To dla mnie zadziałało:

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

polecenie skryptu jest przechwytywane do pliku maszynopisu ansi2txt - konwertuje kod ansi ze znakami zmiany znaczenia, takimi jak kody kolorów, spacje itp., na zwykły tekst, jednak zauważyłem, że kilka znaków ucieczki wciąż pozostaje. col -bp - całkowicie je usunął.

Przetestowałem to na najnowszej dyskotece Ubuntu i działa.


1

W pakiecie na Ubuntu znajduje się ansi2txtpolecenie colorized-logs. Ładnie usuwa kody kolorów ANSI, ale nie radzi sobie z takimi rzeczami, jak paski postępu wytwarzane przez emitowanie ^Hlub ^Mznaki zastępujące tekst w miejscu. col -bporadzi sobie z nimi , więc dla uzyskania najlepszych rezultatów możesz połączyć oba

cat typescript | ansi2txt | col -b

0

Przekonałem się, że wystarczyło użyć cat, by zobaczyć dane wyjściowe scriptw terminalu. To nie pomaga, gdy przekierowanie wyjścia do innego pliku, ale nie sprawiają, że wynik czytelny, w przeciwieństwie do cat -v, col -blub edytora tekstu.

Aby wyeliminować kolory lub zapisać wyniki w pliku, ręcznie skopiuj i wklej dane wyjściowe z catedytora tekstu lub do innego catpolecenia, np .:

cat > endResult << END
<paste_copied_text_here>
END

1
czy twój scriptbieg zawiera dane wyjściowe z dołączonymi kodami kolorów, jak w przypadku PO?
Jeff Schaller

Korzystanie catprezentuje oryginalne kolory, które można usunąć ręcznie kopiując i wklejając. Wykorzystany OP cat -vi col -boba przedstawiają kody zamiast poprawnie sformatowanego wyniku końcowego. Zredagowałem swoją odpowiedź.
Roger Dueck

-2

W odpowiedzi na ostatnią odpowiedź, która używa tr i: cntrl: moglibyśmy zrobić

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

Wydaje mi się, że to działa, ponieważ wszystkie linie generowane przez vi zaczynają się od znaku kontrolnego. Zdarza się również, że usuwam puste linie i linie zaczynające się od tabulatora, chociaż to działa w przypadku tego, co robię. Może istnieje sposób na dopasowanie dowolnego znaku kontrolnego oprócz \ n \ m \ t.

Może możemy wyszukać konkretną postać kontrolną i wygląda na to, że wszystkie niepotrzebne linie generowane przez vi zaczynają się od czegoś, co wygląda jak ^ [. hexdump mówi mi, że pierwsza postać to 1b, więc wydaje się, że to też działa

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

Wygląda to podobnie do odpowiedzi zamieszczonej powyżej, ale nie działa poprawnie, ponieważ po uruchomieniu polecenia niektóre niepotrzebne znaki są już dodawane do wiersza poleceń, tak jakby użytkownik je wpisał.


1
Nie ma „ostatniej odpowiedzi”, ponieważ odpowiedzi mogą i zmieniają kolejność. Powinieneś użyć przycisku „Udostępnij” pod odpowiedzią, do której chcesz się odwoływać, i dołączyć to jako link do swojej odpowiedzi. Oczywiście zakładając, że twoja odpowiedź jest czymś więcej niż komentarzem. W tej chwili nie mogę zidentyfikować, do której z kilku odpowiedzi odnosisz się.
roaima

1
„Może moglibyśmy zrobić ...” Tak, mógł to zrobić - ale byłoby usunąć każdy wiersz , który rozpoczyna się od znaku sterującego . Na przykład na wyjściu ls --color(jak pokazano w pytaniu), rozwiązanie usunie prawie każdy wiersz zawierający informacje. Niedobrze. Ale dzięki za pominięcie bezużytecznego użycia cat. :-) ⁠
G-Man

Czy istnieje sposób na utworzenie klasy znaków, która jest: iscntrl: ale nie: isspace :? Może jakaś składnia taka jak ^ [[: iscntrl:] - [: isspace]]
snaran

-4

tr - tłumaczyć lub usuwać znaki

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

Witamy w Unix Stackexchange! Udzielając odpowiedzi, lepiej jest wyjaśnić, DLACZEGO twoja odpowiedź jest jedna.
Stephen Rauch


3
To tak naprawdę nie będzie działać poprawnie, ponieważ nie usunie 01;34mnp. I usunie końca linii newline (\n).
sorontar
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.