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?

Paweł P.
źródło
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"
Jeff Bowman
źródło
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.

JoeAndrieu
źródło
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)

meustrus
źródło
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'
Grebulon
źródło
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"
edi9999
źródło
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:]'
Dale_Reagan
źródło
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
źródło
@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
Léa Gris
źródło
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)
zstolar
źródło
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
Maxxim
źródło
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.

AB
źródło
-7

To działa dla mnie:

./somescript | cat
spiderlama
źródło
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