Usuwanie kolorów z wydruku


140

Mam skrypt, który generuje dane wyjściowe z kolorami i muszę usunąć kody ANSI.

#!/bin/bash

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

./somescript

Dane wyjściowe (w pliku dziennika):

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

Nie wiedziałem, jak umieścić tutaj znak ESC, więc umieściłem go @w tym miejscu.

Zmieniłem skrypt na:

#!/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"

Ale teraz daje mi (w pliku dziennika):

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

Jak mogę to również usunąć @[60G?

Może jest sposób na całkowite wyłączenie kolorowania dla całego skryptu?


W przypadku węzłów / npm możesz użyć strip-ansi: github.com/chalk/strip-ansi .
— Joshua Pinter

Odpowiedzi:


165

Według Wikipedii , [m|K]w sedkomendzie używasz jest specjalnie zaprojektowany do rączki m(polecenie) i kolor K(na „części” kasowania linii poleceń). Twój skrypt próbuje ustawić bezwzględną pozycję kursora na 60 ( ^[[60G), aby uzyskać wszystkie OK w wierszu, którego sednie obejmuje twoja linia.

(Prawidłowo, [m|K]prawdopodobnie powinno być (m|K)lub [mK], ponieważ nie próbujesz dopasować znaku pionowej kreski. Ale to nie jest teraz ważne).

Jeśli zmienisz ostatnie dopasowanie w poleceniu na [mGK]lub (m|G|K), powinieneś być w stanie złapać tę dodatkową sekwencję sterującą.

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

29
Użytkownicy BSD / OSX: Zwykle nie mamy opcji -r do seda. brew install gnu-sedzainstaluje odpowiednią wersję. Biegnij z gsed.
— Nicolai S

1
Jeśli to zrobię echo "$(tput setaf 1)foo$(tput sgr0) bar" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | cat -A, otrzymam: foo^O bar$Więc wydaje mi się , że niektóre znaki nie są poprawnie usunięte, prawda? Czy wiesz, jak poprawić?
— edi9999

1
@ edi9999 O ile wiem, różnica polega na tym, że ustawienia kolorów powyżej 16 kolorów (jak setafobsługuje) wymagają więcej parametrów niż tylko dwa; moje wyrażenie regularne obsługuje dwa. Zmiana pierwszego ?wyjścia na *powinna pomóc. Obsługa sgr0jest możliwa, ale w oparciu o wyszukiwanie prawdopodobnie wykracza poza zakres tej hakerskiej odpowiedzi opartej na wyrażeniach regularnych.
— Jeff Bowman

OK, dodałem odpowiedź, która dodaje seddo potoku znak „shift in”
— edi9999,

7
To nie działa niezawodnie, ponieważ może istnieć trzecia wartość (ala [38;5;45m). Ta alternatywna odpowiedź działa unix.stackexchange.com/a/55547/168277
— davemyron.

30

Nie mogłem uzyskać przyzwoitych wyników z żadną z pozostałych odpowiedzi, ale zadziałały u mnie następujące:

somescript | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g"

Gdybym tylko usunął znak kontrolny „^ [”, pozostawił on resztę danych koloru, np. „33m”. Dołączenie kodu koloru i „m” załatwiło sprawę. Zastanawiam się, że s / \ x1B // g nie działa, ponieważ \ x1B [31m z pewnością działa z echem.


6
W OSX (BSD sed) użyj -Ezamiast -rrozszerzonego wyrażenia regularnego. Więcej można znaleźć tutaj
— Assambar

musiałem wymienić {1,3}na {,3}(w przeciwnym razie nadal pomijał niektóre elementy sterujące), dzięki za rozwiązanie!
— akcji

6
Ponieważ mogą to być liczby oddzielone średnikami (dla koloru tła, pogrubienia, kursywy itp.). To polecenie działało dla mnie:sed -r "s/[[:cntrl:]]\[([0-9]{1,3};)*[0-9]{1,3}m//g"
— saeedgnu

Ten (z wielu testowanych przeze mnie) działał z wyjściem Ansible, które zostało uruchomione z funkcją unbuffer.
— Martin

23

IMHO, większość tych odpowiedzi zbyt mocno próbuje ograniczyć zawartość kodu ucieczki. W rezultacie brakuje im wspólnych kodów, takich jak [38;5;60m(kolor ANSI pierwszego planu 60 z trybu 256 kolorów).

Wymagają również -ropcji, która włącza rozszerzenia GNU . Nie są one wymagane; po prostu poprawiają odczyt wyrażenia regularnego.

Oto prostsza odpowiedź, która obsługuje 256-kolorowe znaki ucieczki i działa w systemach bez GNU sed:

./somescript | sed 's/\x1B\[[0-9;]\+[A-Za-z]//g'

Spowoduje to wyłapanie wszystkiego, co zaczyna się od [, ma dowolną liczbę miejsc dziesiętnych i średników, a kończy się literą. Powinno to wychwycić wszystkie typowe sekwencje specjalne ANSI .

Dla zabawy, oto większe i bardziej ogólne (ale minimalnie przetestowane) rozwiązanie dla wszystkich możliwych sekwencji ucieczki ANSI :

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

(a jeśli masz problem z SI @ edi9999, dodaj | sed "s/\x0f//g"na końcu; działa to dla każdego znaku kontrolnego , zastępując 0fheksadecymalnym znakiem)


Ten działał ładnie, aby napisać kolor z upiększonego wyjścia Azure az cli.
— volvox

Naprawiono @elig. Okazało się, że miał wiele problemów, począwszy od jakiegoś edytora, który zastąpił wszystkie moje myślniki dziwnymi wersjami Unicode, ale także kilka niewłaściwych znaków ucieczki - |w sedzie, ]wewnątrz klasy znaków w sedzie oraz 'w łańcuchu bash z pojedynczym cudzysłowem. Teraz pracuje dla mnie w bardzo podstawowym przypadku testowym.
— meustrus

20

W przypadku systemu Mac OSX lub BSD

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

1
Dziwne, ten działał dobrze dla Debiana, ale inne powyżej nie.
— cy8g3n

Ten częściowo działał. Jeśli jednak otworzę plik w programie Excel, nadal widzę znak specjalny „?” na końcu każdego wiersza.
— doudy_05

@ doudy_05 Spróbuj przekazać -Eflagę sed, aby włączyć rozszerzone wyrażenie regularne .
— Alexander Zinchenko

14

Miałem też problem, że czasami pojawiała się postać SI.

Stało się tak np. Z tym wejściem: echo "$(tput setaf 1)foo$(tput sgr0) bar"

Oto sposób na usunięcie znaku SI (przesunięcie) (0x0f)

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

2
Nie wiem, dlaczego ta odpowiedź ma tak mały kredyt. To jedyna pracująca dla mnie ...
— m8mble

8

Hmm, nie jestem pewien, czy to zadziała, ale 'tr' usunie '(usunie) kody kontrolne - spróbuj:

./somescript | tr -d '[:cntrl:]'

32
Nagle usuwa też nowe linie
— ruX

Tak, LF i CR (kody) to kody kontrolne; jeśli interesuje Cię więcej niż jedna linia, może to nie być rozwiązanie. Ponieważ wygląda na to, że uruchamiasz program w języku JAVA, zgaduję, że kolory są tam zarządzane; W przeciwnym razie musiałbyś spojrzeć na konfigurację konsoli (tj. Ustawienia terminala / schemat kolorów) i / lub na opcje dla każdego polecenia, które obsługuje 'kolory', tj. Ls --color = never
— Dale_Reagan

3
Podoba mi się ta odpowiedź za jej elegancję, nawet jeśli nie tylko usuwa kolory. Dzięki!
— Johann Philipp Strathausen

7
faktycznie zezwala na kody tam, zobacz ls -l + twoje polecenie:rwxr-xr-x 1 tokra admin 22 Oct 18 14:21 [0m[01;36m/usr/local/opt/gradle[0m -> [01;34m../Cellar/gradle/4.2.1[0m/
— To Kra

7

Miałem podobny problem. Wszystkie znalezione przeze mnie rozwiązania działały dobrze w przypadku kodów kolorów, ale nie usunęły znaków dodanych przez "$(tput sgr0)"(resetowanie atrybutów).

Biorąc na przykład rozwiązanie w komentarzu davemyrona, długość wynikowego ciągu w poniższym przykładzie wynosi 9, a nie 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}

Aby poprawnie działało, wyrażenie regularne musiało zostać rozszerzone, aby pasowało również do sekwencji dodanej przez sgr0(„ \E(B”):

string_sed="$( sed -r "s/\x1B(\[[0-9;]*[JKmsu]|\(B)//g" <<< "${string}" )"

@Jarodiv - dzięki za najbardziej kompleksowe podejście. Wszystkie odpowiedzi udzielone na ten temat dotyczą TYLKO sekwencji sterujących ANSI / VT100 (np .: "\ e [31mHello World \ e [0m"), jednakże nie naprawiaj niczego spowodowanego przez formatowanie tekstu TPUT (np. Tput smso / tput setaf X / tput rmso / tput sgr0). W rezultacie po wszystkich wykonaniach seda w dziennikach pozostał bałagan. To czyste rozwiązanie moich zastosowań!
— bez twarzy

5

O wiele prostsza funkcja w czystym Bash do odfiltrowywania typowych kodów ANSI ze strumienia tekstowego:

# 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
    echo "${line//$'\e'[\[(]*([0-9;])[@-n]/}"
  done
}

Widzieć:

  1. linuxjournal.com: Extended Globbing
  2. gnu.org: Rozszerzenie parametru Bash

1
To nie działa. Testuj z tldr. (Chociaż używam zsh, więc może to być również z tego powodu).
— HappyFace

Rzeczywiście, Zsh nie zrozumie rozszerzonego globowania Basha extglobani prawdopodobnie nie zrozumie całkowicie zastępowania ciągów.
— Léa Gris

Włączyłem rozszerzoną kulkę zsh ... Zamiana łańcucha też powinna być posix?
— HappyFace

Zamiana łańcucha nie jest POSIX. Możesz użyć dowolnej z alternatywnych metod, używając sedwspomnianych tutaj, które będą działać z Zsh.
— Léa Gris

To rozwiązanie ma tę zaletę, że tekst jest buforowany wierszami. Próbowałem z sedem, ale to blokowało moją fajkę.
— Guillermo Prandi

3

Rozwiązanie @ jeff-bowman pomogło mi pozbyć się NIEKTÓRYCH kodów kolorów. Dodałem kolejną małą porcję do wyrażenia regularnego, aby usunąć więcej:

sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" # Original. Removed Red ([31;40m[1m[error][0m)
sed -r "s/\x1B\[([0-9];)?([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" # With an addition, removed yellow and green ([1;33;40m[1m[warning][0m and [1;32;40m[1m[ok][0m)
                ^^^^^^^^^
                remove Yellow and Green (and maybe more colors)

2

Oto czyste rozwiązanie Bash.

Zapisz jako strip-escape-codes.sh, utwórz plik wykonywalny, a następnie uruchom <command-producing-colorful-output> | ./strip-escape-codes.sh.

Zauważ, że usuwa to wszystkie kody / sekwencje ucieczki ANSI. Jeśli chcesz się rozebrać tylko kolory, wymienić [a-zA-Z]się "m".

Bash> = 4,0:

#!/usr/bin/env bash

# Strip ANSI escape codes/sequences [$1: input string, $2: target variable]
function strip_escape_codes() {
    local _input="$1" _i _char _escape=0
    local -n _output="$2"; _output=""
    for (( _i=0; _i < ${#_input}; _i++ )); do
        _char="${_input:_i:1}"
        if (( ${_escape} == 1 )); then
            if [[ "${_char}" == [a-zA-Z] ]]; then
                _escape=0
            fi
            continue
        fi
        if [[ "${_char}" == $'\e' ]]; then
            _escape=1
            continue
        fi
        _output+="${_char}"
    done
}

while read -r line; do
    strip_escape_codes "${line}" line_stripped
    echo "${line_stripped}"
done

Bash <4,0:

#!/usr/bin/env bash

# Strip ANSI escape codes/sequences [$1: input string, $2: target variable]
function strip_escape_codes() {
    local input="${1//\"/\\\"}" output="" i char escape=0
    for (( i=0; i < ${#input}; ++i )); do         # process all characters of input string
        char="${input:i:1}"                       # get current character from input string
        if (( ${escape} == 1 )); then             # if we're currently within an escape sequence, check if
            if [[ "${char}" == [a-zA-Z] ]]; then  # end is reached, i.e. if current character is a letter
                escape=0                          # end reached, we're no longer within an escape sequence
            fi
            continue                              # skip current character, i.e. do not add to ouput
        fi
        if [[ "${char}" == $'\e' ]]; then         # if current character is '\e', we've reached the start
            escape=1                              # of an escape sequence -> set flag
            continue                              # skip current character, i.e. do not add to ouput
        fi
        output+="${char}"                         # add current character to output
    done
    eval "$2=\"${output}\""                       # assign output to target variable
}

while read -r line; do
    strip_escape_codes "${line}" line_stripped
    echo "${line_stripped}"
done

Cóż, to rozwiązanie mogłoby być jeszcze mniej skomplikowane.
— Alexander Zinchenko

1

Kontrowersyjnym pomysłem byłaby rekonfiguracja ustawień terminala dla tego środowiska procesu, aby powiadomić proces, że terminal nie obsługuje kolorów.

TERM=xterm-mono ./somescriptPrzychodzi mi do głowy coś takiego . YMMV z twoim konkretnym systemem operacyjnym i zdolnością twojego skryptu do zrozumienia ustawień kolorów terminala.


-7

To działa dla mnie:

./somescript | cat

3
To zależy od tego, jak somescriptzostanie wdrożone. Może, ale nie musi, rozpoznać, że jego standardowe wyjście to tty. (Słowa „przestępcy” w rzeczywistości trwale wprowadzają do programu kody ucieczki specyficzne dla terminala i strasznie się łamią, gdy są używane na innych terminalach lub w skryptach).
— Toby Speight

Dzięki Toby. Do testowania użyłem pliku manage.py django, ale to, co powiedziałeś, ma sens.
— spiderlama
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.