Farben aus der Ausgabe entfernen

140

Ich habe ein Skript, das eine Ausgabe mit Farben erzeugt, und ich muss die ANSI-Codes entfernen.

#!/bin/bash

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

./somescript

Die Ausgabe ist (in Protokolldatei):

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

Ich wusste nicht, wie ich den ESC-Charakter hier einfügen sollte, also habe ich ihn @an seine Stelle gesetzt.

Ich habe das Skript geändert 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"

Aber jetzt gibt es mir (in Protokolldatei):

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

Wie kann ich das auch entfernen @[60G?

Vielleicht gibt es eine Möglichkeit, die Farbgebung für das gesamte Skript vollständig zu deaktivieren?

Pawel P.
quelle
Für node / npm können Sie Folgendes verwenden strip-ansi: github.com/chalk/strip-ansi .
Joshua Pinter

Antworten:

164

Laut Wikipedia , die [m|K]in dem sedist Befehl Sie verwenden speziell Griff entworfen m(die Farbe Befehl) und K(der „Lösch Teil der Zeile“ Befehl). Ihr Skript versucht, die absolute Cursorposition auf 60 ( ^[[60G) zu setzen, um alle OKs in einer Zeile zu erhalten, die Ihre sedZeile nicht abdeckt.

(Richtig [m|K]sollte es wahrscheinlich sein (m|K)oder [mK], weil Sie nicht versuchen, einem Pipe-Charakter zu entsprechen. Aber das ist momentan nicht wichtig.)

Wenn Sie diese letzte Übereinstimmung in Ihrem Befehl auf [mGK]oder umschalten, (m|G|K)sollten Sie in der Lage sein, diese zusätzliche Kontrollsequenz zu erfassen.

./somescript | sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]//g"
Jeff Bowman
quelle
29
BSD / OSX-Benutzer: Wir haben normalerweise nicht die Option -r zum Sedieren. brew install gnu-sedinstalliert eine fähige Version. Laufen Sie mit gsed.
Nicolai S
1
Wenn ich das tue echo "$(tput setaf 1)foo$(tput sgr0) bar" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | cat -A, bekomme ich: foo^O bar$Ich denke, einige Zeichen werden nicht richtig entfernt, oder? Wissen Sie, wie man korrigiert?
edi9999
1
@ edi9999 Soweit ich das beurteilen kann, besteht der Unterschied darin, dass Farbeinstellungen über 16 Farben (als setafUnterstützung) mehr Parameter als nur zwei erfordern. Mein Regex unterstützt zwei. Ändern des ersten ?out für *soll helfen. Die Handhabung sgr0ist möglich, aber basierend auf einer Suche wächst sie wahrscheinlich außerhalb des Rahmens dieser auf Hacky Regex basierenden Antwort.
Jeff Bowman
Ok, ich habe eine Antwort hinzugefügt, die sedder Pipe ein hinzufügt , um das "Shift-in" -Zeichen zu
entfernen
7
Dies funktioniert nicht zuverlässig, da es einen dritten Wert geben kann (ala [38;5;45m). Diese alternative Antwort funktioniert unix.stackexchange.com/a/55547/168277
davemyron
30

Ich konnte mit keiner der anderen Antworten anständige Ergebnisse erzielen, aber Folgendes funktionierte für mich:

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

Wenn ich nur das Steuerzeichen "^ [" entfernt habe, sind die restlichen Farbdaten übrig geblieben, z. B. "33m". Das Einfügen des Farbcodes und "m" hat den Trick gemacht. Ich bin verwirrt mit s / \ x1B // g funktioniert nicht, weil \ x1B [31m sicherlich mit Echo funktioniert.

JoeAndrieu
quelle
6
Verwenden Sie unter OSX (BSD sed) -Eanstelle von -rfür erweiterten Regex. Weitere können hier gefunden werden
Assambar
Ich hatte zu ersetzen , {1,3}um {,3}(sonst war es noch einige Kontrollen Skipping), Dank für Ihre Lösung!
Actionless
6
Da es sich möglicherweise um mehrere durch Semikolons getrennte Zahlen handelt (für Hintergrundfarbe, Fettdruck, Kursivschrift usw.). Dieser Befehl funktionierte für mich:sed -r "s/[[:cntrl:]]\[([0-9]{1,3};)*[0-9]{1,3}m//g"
saeedgnu
Dieser (von den vielen, die ich getestet habe) arbeitete mit Ansible-Ausgaben, die mit Entpuffer ausgeführt wurden.
Martin
23

IMHO versuchen die meisten dieser Antworten zu sehr, den Inhalt des Escape-Codes einzuschränken. Infolgedessen fehlen ihnen häufig verwendete Codes wie [38;5;60m(Vordergrund-ANSI-Farbe 60 aus dem 256-Farben-Modus).

Sie erfordern auch die -rOption, die GNU-Erweiterungen aktiviert . Diese sind nicht erforderlich; Sie lassen den regulären Ausdruck nur besser lesen.

Hier ist eine einfachere Antwort, die die 256-Farben-Escapezeichen behandelt und auf Systemen mit Nicht-GNU funktioniert sed :

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

Dies fängt alles auf, was mit beginnt [ , eine beliebige Anzahl von Dezimalstellen und Semikolons hat und mit einem Buchstaben endet. Dies sollte eine der gängigen ANSI-Escape-Sequenzen abfangen .

Für Funsies ist hier eine größere und allgemeinere (aber minimal getestete) Lösung für alle denkbaren ANSI-Escape-Sequenzen :

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

(und wenn Sie das SI-Problem von @ edi9999 haben, fügen Sie | sed "s/\x0f//g"es am Ende hinzu; dies funktioniert für jedes Steuerzeichen, indem Sie es 0fdurch das Hex des unerwünschten Zeichens ersetzen. )

Meustrus
quelle
Dieser hat gut funktioniert, um die Farbe aus der hübschen Azure Az Cli-Ausgabe herauszuholen.
Volvox
@Relig behoben. Es stellte sich heraus, dass es eine Reihe von Problemen gab, angefangen damit, dass ein Editor alle meine Bindestriche durch seltsame Unicode-Versionen ersetzte, aber auch eine Reihe von unzulässigen Fluchtversuchen - |in sed, ]innerhalb einer Zeichenklasse in sed und 'in einer einfach zitierten Bash-Zeichenfolge. Es funktioniert jetzt für mich für einen sehr einfachen Testfall.
Meustrus
20

Für Mac OSX oder BSD verwenden

./somescript | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g'
Grebulon
quelle
1
Seltsam, dieser funktionierte gut für Debian, aber der andere oben tat es nicht.
Cy8g3n
Dieser hat teilweise funktioniert. Wenn ich jedoch eine Datei in Excel öffne, wird immer noch dieses Sonderzeichen "?" am Ende jeder Zeile.
doudy_05
@ doudy_05 Versuchen Sie, das -EFlag für sed zu übergeben, um den erweiterten regulären Ausdruck zu aktivieren.
Alexander Zinchenko
14

Ich hatte auch das Problem, dass manchmal das SI-Zeichen auftauchte.

Es ist zum Beispiel mit dieser Eingabe passiert: echo "$(tput setaf 1)foo$(tput sgr0) bar"

Hier ist eine Möglichkeit, auch das SI-Zeichen (Shift In) (0x0f) zu entfernen.

./somescript | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | sed "s/\x0f//g"
edi9999
quelle
2
Ich bin mir nicht sicher, warum diese Antwort so wenig Anerkennung erhält. Dies ist der einzige, der für mich arbeitet ...
m8mble
8

Hmm, ich bin mir nicht sicher, ob dies für Sie funktioniert, aber 'tr' entfernt (löscht) Steuercodes - versuchen Sie:

./somescript | tr -d '[:cntrl:]'
Dale_Reagan
quelle
32
Plötzlich werden auch neue Zeilen entfernt
ruX
Ja, LF und CR (Codes) sind Steuercodes. Wenn Sie an mehr als einer Zeile interessiert sind, ist dies möglicherweise keine Lösung. Da es den Anschein hat, dass Sie ein JAVA-Programm ausführen, werde ich vermuten, dass die Farben von dort aus verwaltet werden. Andernfalls müssten Sie sich Ihr Konsolen-Setup (dh Terminaleinstellungen / Farbschema) und / oder die Optionen für jeden Befehl ansehen, der 'Farben' unterstützt, dh ls --color = never
Dale_Reagan
3
Ich mag diese Antwort wegen ihrer Eleganz, auch wenn sie mehr als nur Farben entfernt. Vielen Dank!
Johann Philipp Strathausen
7
es ließ tatsächlich Codes dort, siehe ls -l + Ihr Befehl: 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/
Zu Kra
7

Ich hatte ein ähnliches Problem. Alle Lösungen, die ich gefunden habe, funktionierten gut für die Farbcodes, entfernten jedoch nicht die von hinzugefügten Zeichen"$(tput sgr0)" (Zurücksetzen von Attributen) .

Nehmen wir zum Beispiel die Lösung im Kommentar von Davemyron, die Länge der resultierenden Zeichenfolge im folgenden Beispiel beträgt 9, nicht 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}

Um richtig zu funktionieren, musste der Regex erweitert werden, um auch der von hinzugefügten Sequenz zu entsprechen sgr0 (" \E(B") :

string_sed="$( sed -r "s/\x1B(\[[0-9;]*[JKmsu]|\(B)//g" <<< "${string}" )"
Jarodiv
quelle
@ Jarodiv - danke für den umfassendsten Ansatz. Alle Antworten zu diesem Thema beziehen sich NUR auf ANSI / VT100-Steuerungssequenzen (z. B. "\ e [31mHello World \ e [0m"), beheben jedoch nichts, was durch die TPUT-Textformatierung verursacht wird (z. B. tput smso / tput setaf X. / tput rmso / tput sgr0). Infolgedessen gab es nach allen "sed" -Ausführungen noch ein anderes Durcheinander in den Protokollen. Dies ist eine reine Lösung für meine Anwendungsfälle!
gesichtslos
5

Viel einfachere Funktion in reinem Bash zum Herausfiltern gängiger ANSI-Codes aus einem Textstrom:

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

Sehen:

  1. linuxjournal.com: Erweitertes Globbing
  2. gnu.org: Erweiterung der Bash-Parameter
Léa Gris
quelle
1
Das funktioniert nicht. Test mit tldr. (Obwohl ich zsh benutze, könnte es auch daran liegen.)
HappyFace
In der Tat wird Zsh Bashs erweitertes Globing nicht verstehen extgloboder wahrscheinlich auch nicht das Ersetzen von Strings insgesamt.
Léa Gris
Ich habe den Extendedglob von zsh aktiviert ... String-Ersetzung sollte auch posix sein?
HappyFace
Das Ersetzen von Zeichenfolgen ist nicht POSIX. Sie können jede der sedhier genannten alternativen Methoden verwenden , die mit Zsh funktionieren.
Léa Gris
Diese Lösung hat den Vorteil, dass der Text zeilenpuffert wird. Ich habe es mit sed versucht, aber es hat meine Pipe blockiert.
Guillermo Prandi
3

Die Lösung von @ jeff-bowman hat mir geholfen, einige der Farbcodes loszuwerden. Ich habe der Regex eine weitere kleine Portion hinzugefügt, um weitere zu entfernen:

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
quelle
2

Hier ist eine reine Bash-Lösung.

Speichern unter strip-escape-codes.sh, ausführbar machen und dann ausführen <command-producing-colorful-output> | ./strip-escape-codes.sh.

Beachten Sie, dass dadurch alle ANSI-Escape-Codes / Sequenzen entfernt werden. Wenn Sie nur Farben entfernen möchten, ersetzen Sie diese [a-zA-Z]durch "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
quelle
Nun, diese Lösung könnte noch weniger kompliziert sein.
Alexander Zinchenko
1

Die umstrittene Idee wäre, die Terminaleinstellungen für diese Prozessumgebung neu zu konfigurieren, um den Prozess darüber zu informieren, dass das Terminal keine Farben unterstützt.

Mir TERM=xterm-mono ./somescriptfällt so etwas ein. YMMV mit Ihrem spezifischen Betriebssystem und der Fähigkeit Ihres Skripts, die Farbeinstellungen des Terminals zu verstehen.

AB
quelle
-7

Das funktioniert bei mir:

./somescript | cat
Spiderlama
quelle
3
Das hängt davon ab, wie somescriptes umgesetzt wird. Es kann oder kann nicht erkennen, dass seine Standardausgabe ein tty ist. (Die Wörter Täter codieren tatsächlich terminalspezifische Escape-Codes fest in das Programm und brechen schrecklich, wenn sie auf anderen Terminals oder in Skripten verwendet werden.)
Toby Speight
Danke Toby. Ich habe djangos manage.py zum Testen verwendet, aber was Sie gesagt haben, macht Sinn.
Spiderlama