Suppression des couleurs de la sortie

141

J'ai un script qui produit une sortie avec des couleurs et je dois supprimer les codes ANSI.

#!/bin/bash

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

./somescript

La sortie est (dans le fichier journal):

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

Je ne savais pas comment mettre le caractère ESC ici, alors j'ai mis @à sa place.

J'ai changé le script en:

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

Mais maintenant, cela me donne (dans le fichier journal):

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

Comment puis-je également supprimer ce ' @[60G?

Peut-être existe-t-il un moyen de désactiver complètement la coloration pour tout le script?

Pawel P.
la source
Pour node / npm, vous pouvez utiliser strip-ansi: github.com/chalk/strip-ansi .
Joshua Pinter le

Réponses:

166

Votre script essaie de définir la position absolue du curseur sur 60 ( ) pour obtenir tous les OK dans une ligne, que votre ligne ne couvre pas.[m|K]sedmK^[[60Gsed

(Correctement, [m|K]devrait probablement être (m|K)ou [mK], parce que vous n'essayez pas de faire correspondre un caractère de pipe. Mais ce n'est pas important pour le moment.)

Si vous basculez cette dernière correspondance dans votre commande sur [mGK]ou (m|G|K), vous devriez pouvoir attraper cette séquence de contrôle supplémentaire.

./somescript | sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]//g"
Jeff Bowman
la source
29
Utilisateurs BSD / OSX: nous n'avons généralement pas l'option -r pour sed. brew install gnu-sedinstallera une version compatible. Courez avec gsed.
Nicolai S
1
Si je le fais echo "$(tput setaf 1)foo$(tput sgr0) bar" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | cat -A, j'obtiens: foo^O bar$Donc je suppose que certains caractères ne sont pas correctement supprimés, non? Savez-vous comment corriger?
edi9999
1
@ edi9999 Autant que je sache, la différence est que les réglages de couleur au-delà de 16 couleurs (en tant que setafsupports) nécessitent plus de paramètres que deux; mon regex en prend en charge deux. Changer le premier ?pour *devrait aider. La gestion sgr0est possible, mais en fonction d'une recherche, elle se développe probablement en dehors de la portée de cette réponse hacky regex.
Jeff Bowman
Ok, j'ai ajouté une réponse qui ajoute un sedau tuyau pour
supprimer le
7
Cela ne fonctionne pas de manière fiable car il peut y avoir une troisième valeur (ala [38;5;45m). Cette réponse alternative fonctionne unix.stackexchange.com/a/55547/168277
davemyron
30

Je n'ai pu obtenir de résultats décents avec aucune des autres réponses, mais ce qui suit a fonctionné pour moi:

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

Si j'ai seulement supprimé le caractère de contrôle "^ [", il a laissé le reste des données de couleur, par exemple, "33m". Inclure le code couleur et "m" a fait l'affaire. Je suis perplexe avec s / \ x1B // g ne fonctionne pas parce que \ x1B [31m fonctionne certainement avec echo.

JoeAndrieu
la source
6
Sous OSX (BSD sed), utilisez à la -Eplace de -rpour une expression régulière étendue. Plus d'informations peuvent être trouvées ici
Assambar
je devais remplacer {1,3}à {,3}(sinon il sautillait encore des contrôles), merci pour votre solution!
2018 sans action
6
Puisqu'il peut s'agir de plusieurs nombres séparés par des points-virgules (pour la couleur d'arrière-plan, gras, italique, etc.). Cette commande a fonctionné pour moi:sed -r "s/[[:cntrl:]]\[([0-9]{1,3};)*[0-9]{1,3}m//g"
saeedgnu
Celui-ci (parmi les nombreux que j'ai testés) fonctionnait avec une sortie Ansible qui avait été exécutée avec un buffer.
Martin
23

À mon humble avis, la plupart de ces réponses essaient trop fort de restreindre ce qui se trouve à l'intérieur du code d'échappement. En conséquence, ils finissent par manquer des codes communs tels que [38;5;60m(couleur ANSI de premier plan 60 à partir du mode 256 couleurs).

Ils nécessitent également l' -roption qui active les extensions GNU . Ce ne sont pas obligatoires; ils améliorent simplement la lecture de l'expression régulière.

Voici une réponse plus simple qui gère les échappements de 256 couleurs et fonctionne sur les systèmes avec non-GNU sed:

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

Cela attrapera tout ce qui commence par [, a un nombre quelconque de décimales et de points-virgules et se termine par une lettre. Cela devrait intercepter l'une des séquences d'échappement ANSI courantes .

Pour le plaisir, voici une solution plus large et plus générale (mais peu testée) pour toutes les séquences d'échappement ANSI imaginables :

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

(et si vous avez le problème SI de @ edi9999, ajoutez | sed "s/\x0f//g"à la fin; cela fonctionne pour tout caractère de contrôle en le remplaçant 0fpar l'hexagone du caractère indésirable)

meustrus
la source
Celui-ci fonctionnait bien pour faire ressortir la couleur de la chaîne de sortie Azure az cli jolie.
volvox
Correction de @elig. Il s'est avéré qu'il y avait un certain nombre de problèmes, à commencer par un éditeur remplaçant tous mes tirets par des versions unicode étranges, mais aussi un tas d'échappements inappropriés - |dans sed, ]dans une classe de caractères dans sed et 'dans une chaîne bash entre guillemets simples. Cela fonctionne maintenant pour moi pour un cas de test très basique.
meustrus
20

Pour l'utilisation de Mac OSX ou BSD

./somescript | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g'
Grebulon
la source
1
Étrange, celui-ci fonctionnait bien pour Debian, mais les autres ci-dessus ne le faisaient pas.
cy8g3n
Celui-ci a partiellement fonctionné. Cependant, si j'ouvre un fichier dans Excel, je vois toujours ce caractère spécial "?" à la fin de chaque ligne.
doudy_05
@ doudy_05 Essayez de passer le -Edrapeau pour sed pour activer l'expression rationnelle étendue.
Alexander Zinchenko le
14

J'ai aussi eu le problème que parfois, le caractère SI apparaissait.

C'est arrivé par exemple avec cette entrée: echo "$(tput setaf 1)foo$(tput sgr0) bar"

Voici un moyen de supprimer également le caractère SI (shift in) (0x0f)

./somescript | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | sed "s/\x0f//g"
edi9999
la source
2
Je ne sais pas pourquoi cette réponse reçoit si peu de crédit. C'est le seul qui fonctionne pour moi ...
m8mble
8

Hmm, je ne sais pas si cela fonctionnera pour vous, mais 'tr' va 'supprimer' (supprimer) les codes de contrôle - essayez:

./somescript | tr -d '[:cntrl:]'
Dale_Reagan
la source
32
Soudain, il supprime également de nouvelles lignes
ruX
Oui, LF et CR (codes) sont des codes de contrôle; si vous êtes intéressé par plus d'une ligne, cela peut ne pas être une solution. Puisqu'il semble que vous exécutez un programme JAVA, je suppose que les couleurs sont gérées à partir de là; Sinon, vous devrez regarder la configuration de votre console (c'est-à-dire les paramètres du terminal / le jeu de couleurs) et / ou les options pour chaque commande qui prend en charge les 'couleurs', c'est-à-dire ls --color = never
Dale_Reagan
3
J'aime cette réponse pour son élégance, même si elle fait plus que supprimer les couleurs. Merci!
Johann Philipp Strathausen
7
il laisse effectivement les codes là-bas, voir ls -l + votre commande: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

J'avais un problème similaire. Toutes les solutions que j'ai trouvées fonctionnaient bien pour les codes de couleur mais n'ont pas supprimé les caractères ajoutés par "$(tput sgr0)"(réinitialisation des attributs).

En prenant, par exemple, la solution dans le commentaire de davemyron, la longueur de la chaîne résultante dans l'exemple ci-dessous est 9, pas 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}

Pour fonctionner correctement, l'expression régulière devait être étendue pour correspondre également à la séquence ajoutée par sgr0(" \E(B"):

string_sed="$( sed -r "s/\x1B(\[[0-9;]*[JKmsu]|\(B)//g" <<< "${string}" )"
Jarodiv
la source
@Jarodiv - merci pour l'approche la plus complète. Toutes les réponses fournies à ce sujet concernent UNIQUEMENT les séquences de contrôle ANSI / VT100 (ex: "\ e [31mHello World \ e [0m"), mais ne corrigent rien du fait du formatage de texte TPUT (ex: tput smso / tput setaf X / tput rmso / tput sgr0). En conséquence, après toutes les exécutions «sed», il restait un autre désordre dans les journaux. C'est une pure solution à mes cas d'utilisation!
sans visage
5

Fonction beaucoup plus simple dans pure Bash pour filtrer les codes ANSI communs d'un flux de texte:

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

Voir:

  1. linuxjournal.com: Globbing étendu
  2. gnu.org: Extension des paramètres Bash
Léa Gris
la source
1
Cela ne marche pas. Testez avec tldr. (Bien que j'utilise zsh, cela pourrait aussi être à cause de cela.)
HappyFace
En effet, Zsh ne comprendra pas la globalisation étendue de Bash extglobou ne comprendra probablement pas non plus le remplacement de chaîne.
Léa Gris
J'ai activé le extendedglob de zsh ... Le remplacement de la chaîne devrait aussi être posix?
HappyFace
Le remplacement de chaîne n'est pas POSIX. Vous pouvez utiliser l'une des méthodes alternatives sedmentionnées ici qui fonctionnera avec Zsh.
Léa Gris
Cette solution présente l'avantage de mettre en tampon la ligne du texte. J'ai essayé avec sed mais il tamponnait ma pipe.
Guillermo Prandi
3

La solution de @ jeff-bowman m'a aidé à me débarrasser de CERTAINS codes de couleurs. J'ai ajouté une autre petite partie à l'expression régulière afin d'en supprimer un peu plus:

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
la source
2

Voici une solution pure Bash.

Enregistrez sous strip-escape-codes.sh, rendez exécutable, puis exécutez <command-producing-colorful-output> | ./strip-escape-codes.sh.

Notez que cela supprime tous les codes / séquences d'échappement ANSI. Si vous souhaitez décaper uniquement les couleurs, remplacez-les [a-zA-Z]par "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
la source
Eh bien, cette solution pourrait être encore moins compliquée.
Alexander Zinchenko le
1

L'idée controversée serait de reconfigurer les paramètres du terminal pour cet environnement de processus afin de faire savoir au processus que le terminal ne prend pas en charge les couleurs.

Quelque chose comme TERM=xterm-mono ./somescriptça me vient à l'esprit. YMMV avec votre système d'exploitation spécifique et la capacité de votre script à comprendre les paramètres de couleur du terminal.

UN B
la source
-7

Cela fonctionne pour moi:

./somescript | cat
Spiderlama
la source
3
Cela dépend de la manière dont somescriptest mis en œuvre. Il peut reconnaître ou non que sa sortie standard est un tty. (Les mots délinquants codent en dur des codes d'échappement spécifiques aux terminaux dans le programme, et se cassent horriblement lorsqu'ils sont utilisés sur d'autres terminaux ou dans des scripts).
Toby Speight
Merci Toby. J'ai utilisé le manage.py de django pour tester, mais ce que vous avez dit est logique.
spiderlama