Rimozione dei colori dall'output


141

Ho alcuni script che producono output con colori e ho bisogno di rimuovere i codici ANSI.

#!/bin/bash

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

./somescript

L'output è (nel file di registro):

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

Non sapevo come mettere il personaggio ESC qui, quindi ho messo @al suo posto.

Ho cambiato lo script in:

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

Ma ora mi dà (nel file di registro):

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

Come posso rimuovere anche questo " @[60G?

Forse c'è un modo per disabilitare completamente la colorazione per l'intero script?


Per nodo / npm, è possibile utilizzare strip-ansi: github.com/chalk/strip-ansi .
— Joshua Pinter

Risposte:


166

Secondo Wikipedia , il [m|K]nel sedcomando che si sta utilizzando è specificamente progettato per gestire m(il comando colore) e K(la "parte cancellazione della linea di comando"). Lo script sta cercando di impostare la posizione assoluta del cursore su 60 ( ^[[60G) per ottenere tutti gli OK in una riga, che la sedlinea non copre.

(Giustamente, [m|K]probabilmente dovrebbe essere (m|K)o [mK], perché non stai cercando di abbinare un personaggio di pipa. Ma non è importante in questo momento.)

Se cambi l'ultima partita nel tuo comando su [mGK]o (m|G|K), dovresti essere in grado di catturare quella sequenza di controllo extra.

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

29
Utenti BSD / OSX: di solito non abbiamo l'opzione -r per sed. brew install gnu-sedinstallerà una versione capace. Corri con gsed.
— Nicolai S,

1
Se lo faccio echo "$(tput setaf 1)foo$(tput sgr0) bar" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | cat -A, ottengo: foo^O bar$Quindi immagino che alcuni personaggi non vengano rimossi correttamente, giusto? Sai come correggere?
— edi9999,

1
@ edi9999 Per quanto ne so, la differenza è che le impostazioni dei colori oltre i 16 colori (come setafsupporti) richiedono più parametri rispetto a solo due; il mio regex ne supporta due. Cambiare il primo ?per *dovrebbe aiutare. La gestione sgr0è possibile ma sulla base di una ricerca probabilmente cresce al di fuori dell'ambito di questa risposta basata sul regex.
— Jeff Bowman,

Ok, ho aggiunto una risposta che aggiunge un carattere sedalla pipe per eliminare il carattere "shift in"
— edi9999

7
Questo non funziona in modo affidabile in quanto può esserci un terzo valore (ala [38;5;45m). Questa risposta alternativa funziona unix.stackexchange.com/a/55547/168277
— davemyron

30

Non sono riuscito a ottenere risultati decenti da nessuna delle altre risposte, ma quanto segue ha funzionato per me:

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

Se ho rimosso solo il carattere di controllo "^ [", ha lasciato il resto dei dati del colore, ad esempio "33m". Compreso il codice colore e "m" ha fatto il trucco. Sono perplesso con s / \ x1B // g non funziona perché \ x1B [31m sicuramente funziona con l'eco.


6
Su OSX (BSD sed), utilizzare -Einvece di -rregex esteso. Altro potrebbe essere trovato qui
— Assambar

ho dovuto sostituire {1,3}a {,3}(altrimenti era ancora saltando alcuni controlli), grazie per la vostra soluzione!
— azione il

6
Dal momento che potrebbero essere più numeri separati da punti e virgola (per il colore di sfondo, grassetto, corsivo, ecc ...). Questo comando ha funzionato per me:sed -r "s/[[:cntrl:]]\[([0-9]{1,3};)*[0-9]{1,3}m//g"
— saeedgnu,

Questo (dei tanti che ho testato) ha funzionato con l'output Ansible che era stato eseguito con un buffer.
— Martin,

23

IMHO, la maggior parte di queste risposte si sforza troppo per limitare ciò che è all'interno del codice di escape. Di conseguenza, finiscono per perdere codici comuni come[38;5;60m (primo piano ANSI colore 60 dalla modalità 256 colori).

Richiedono anche l' -ropzione che abilita estensioni GNU . Questi non sono richiesti; fanno solo leggere meglio il regex.

Ecco una risposta più semplice che gestisce le fughe a 256 colori e funziona su sistemi con non GNU sed:

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

Questo prenderà tutto ciò che inizia con [, ha un numero qualsiasi di decimali e punti e virgola e termina con una lettera. Questo dovrebbe catturare una qualsiasi delle sequenze di escape ANSI comuni .

Per i divertimenti, ecco una soluzione più ampia e più generale (ma minimamente testata) per tutte le possibili sequenze di fuga ANSI :

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

(e se hai il problema SI di @ edi9999, aggiungi | sed "s/\x0f//g"alla fine; questo funziona per qualsiasi carattere di controllo sostituendolo 0fcon l'esagono del carattere indesiderato)


Questo ha funzionato bene per mettere insieme il colore dall'output predefinito di Azure az cli.
— volvox,

Risolto @elig. Si è scoperto che aveva una serie di problemi, a cominciare da alcuni editor che sostituivano tutti i miei trattini con strane versioni unicode, ma anche un mucchio di escape impropri: |in sed, ]all'interno di una classe di caratteri in sed e 'in una stringa bash a virgoletta singola. Ora funziona per me per un caso di test molto semplice.
— Meustrus

20

Per Mac OSX o BSD utilizzare

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

1
Strano, questo ha funzionato bene per Debian ma altri sopra non lo hanno fatto.
— cy8g3n,

Questo ha funzionato parzialmente. Tuttavia, se apro un file in Excel, vedo ancora questo carattere speciale "?" alla fine di ogni riga.
— doudy_05,

@ doudy_05 Prova a passare il -Eflag per sed per abilitare regexp esteso.
— Alexander Zinchenko,

14

Ho anche avuto il problema che a volte appariva il personaggio SI.

È successo ad esempio con questo input: echo "$(tput setaf 1)foo$(tput sgr0) bar"

Ecco un modo per eliminare anche il carattere SI (shift in) (0x0f)

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

2
Non sono sicuro del perché questa risposta riceva così poco credito. Questo è l'unico che funziona per me ...
— m8mble

8

Hmm, non sono sicuro se questo funzionerà per te, ma 'tr' eliminerà '(eliminerà) i codici di controllo - prova:

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

32
All'improvviso rimuove anche nuove linee
— ruX,

Sì, LF e CR (codici) sono codici di controllo; se sei interessato a più di una riga, questa potrebbe non essere una soluzione. Dal momento che sembra che tu stia eseguendo un programma JAVA, immagino che i colori siano gestiti da lì; Altrimenti dovresti guardare la configurazione della tua console (ovvero le impostazioni del terminale / combinazione di colori) e / o le opzioni per ciascun comando che supporta i 'colori', cioè ls --color = never
— Dale_Reagan

3
Mi piace questa risposta per la sua eleganza, anche se non si limita a rimuovere i colori. Grazie!
— Johann Philipp Strathausen,

7
in realtà lascia i codici lì, vedi ls -l + il tuo comando: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/
— A Kra il

7

Ho avuto un problema simile. Tutte le soluzioni che ho trovato hanno funzionato bene per i codici colore ma non hanno rimosso i caratteri aggiunti da "$(tput sgr0)"(reimpostazione degli attributi).

Prendendo, ad esempio, la soluzione nel commento di davemyron la lunghezza della stringa risultante nell'esempio seguente è 9, non 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}

Per funzionare correttamente, è stato necessario estendere il regex affinché corrispondesse anche alla sequenza aggiunta da sgr0(" \E(B"):

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

@Jarodiv - grazie per l'approccio più comprensivo. Tutte le risposte fornite su questo argomento riguardano SOLO le sequenze di controllo ANSI / VT100 (ad es. "\ E [31mHello World \ e [0m"), tuttavia non correggere alcunché causato dalla formattazione del testo TPUT (es: tput smso / tput setaf X / tput rmso / tput sgr0). Di conseguenza, dopo tutte le esecuzioni 'sed', nei registri rimanevano altri problemi. Questa è una soluzione pura ai miei casi d'uso!
— senza volto,

5

Funzione molto più semplice in puro Bash per filtrare i codici ANSI comuni da un flusso di testo:

# 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
}

Vedere:

  1. linuxjournal.com: Extended Globbing
  2. gnu.org: espansione dei parametri di Bash

1
Questo non funziona Prova con tldr. (Anche se uso zsh, quindi potrebbe essere anche per questo.)
— HappyFace,

In effetti, Zsh non capirà l'estensione globale di Bash extglobo probabilmente nemmeno capirà del tutto la sostituzione delle stringhe.
— Léa Gris,

Ho abilitato l'estensione estesa di zsh ... Anche la sostituzione delle stringhe dovrebbe essere posix?
— HappyFace,

La sostituzione della stringa non è POSIX. Puoi usare uno dei metodi alternativi usando sedmenzionato qui che funzionerà con Zsh.
— Léa Gris,

Questa soluzione ha il vantaggio di eseguire il buffering di riga del testo. Ho provato con sed ma stava bloccando il blocco della mia pipa.
— Guillermo Prandi,

3

La soluzione di @ jeff-bowman mi ha aiutato a sbarazzarmi di ALCUNI codici colore. Ho aggiunto un'altra piccola parte al regex per rimuoverne un po 'di più:

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

Ecco una soluzione Bash pura.

Salva come strip-escape-codes.sh, rendi eseguibile e poi esegui<command-producing-colorful-output> | ./strip-escape-codes.sh .

Si noti che questo elimina tutti i codici / sequenze di escape ANSI. Se si desidera rimuovere solo i colori, sostituirlo [a-zA-Z]con"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

Bene, questa soluzione potrebbe essere ancora meno complicata.
— Alexander Zinchenko,

1

L'idea controversa sarebbe quella di riconfigurare le impostazioni del terminale per questo ambiente di processo per far sapere al processo che il terminale non supporta i colori.

TERM=xterm-mono ./somescriptMi viene in mente qualcosa di simile . YMMV con il tuo sistema operativo specifico e la capacità del tuo script di comprendere le impostazioni del colore del terminale.


-7

Questo funziona per me:

./somescript | cat

3
Dipende da come somescriptviene implementato. Può riconoscere o meno che il suo output standard è un tty. (Le parole trasgressori in realtà codificano nel programma codici di escape specifici del terminale e si rompono in modo orribile se utilizzati su altri terminali o negli script).
— Toby Speight,

Grazie Toby. Ho usato django's manage.py per testare, ma ciò che hai detto ha senso.
— spiderlama,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.