web-dev-qa-db-fra.com

Supprimer les couleurs de la sortie

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, il me donne (dans le fichier journal):

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

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

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

111
Pawel P.

selon Wikipedia , le [m|K] dans la commande sed que vous utilisez est spécialement conçue pour gérer m (la commande de couleur) et K (la commande "effacer une partie de la ligne"). Votre script tente de définir la position absolue du curseur sur 60 (^[[60G) pour obtenir tous les OK dans une ligne, ce que votre ligne sed ne couvre pas.

(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 passez cette dernière correspondance dans votre commande à [mGK] ou (m|G|K), vous devriez pouvoir capturer cette séquence de contrôle supplémentaire.

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

Je ne pouvais obtenir de résultats décents d'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 je ne retirais que le caractère de contrôle "^ [", il restait le reste des données de couleur, par exemple, "33m". Y compris le code de couleur et "m" a fait l'affaire. Je suis perplexe avec s/\ x1B // g ne fonctionne pas car\x1B [31m fonctionne certainement avec echo.

23
JoeAndrieu

Pour Mac OSX ou BSD, utilisez

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

J'ai également eu le problème que parfois, le caractère SI est apparu.

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 (décalage) (0x0f)

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

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

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

IMHO, la plupart de ces réponses essaient trop de restreindre le contenu du code d'échappement. En conséquence, il leur manque des codes communs tels que [38;5;60m (Couleur ANSI au premier plan 60 à partir du mode 256 couleurs).

Ils ont également besoin de l’option -r Qui active extensions GN . Ce ne sont pas nécessaires; ils font juste la regex mieux lire.

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 va attraper 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 n'importe lequel des séquences d'échappement ANSI communes .

Pour les funsies, voici une solution plus large et plus générale (mais non 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 remplaçant 0f par l'hex du caractère non souhaité)

9
meustrus

J'avais un problème similaire. Toutes les solutions que j'ai trouvées ont bien fonctionné 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, et 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}

Afin de fonctionner correctement, la regex 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}" )"
7
Jarodiv

Fonction beaucoup plus simple dans Bash pur pour filtrer les codes ANSI courants 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: Extended Globbing
  2. gnu.org: extension du paramètre Bash
3
Léa Gris

La solution de @ jeff-bowman m'a aidé à me débarrasser de CERTAINS codes de couleur. J'ai ajouté une autre petite portion à la regex 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)
3
zstolar

Si on a besoin de faire ceci dans un script Bash, la fonction suivante peut être utilisée:

# Strip escape codes/sequences [$1: input, $2: target variable]
function strip_escape_codes() {
    local input="${1//\"/\\\"}" output="" i char within_code=0
    for ((i=0; i < ${#input}; ++i)); do
        char="${input:i:1}"                     # get current character
        if (( ${within_code} == 1 )); then      # if we're currently within an escape code, check if end of
            case "${char}" in                   # code is reached, i.e. if current character is a letter
                [a-zA-Z]) within_code=0 ;;      # we're no longer within an escape code
            esac
            continue
        fi
        if [[ "${char}" == $'\e' ]]; then       # if current character is '\e', we've reached an escape code
            within_code=1                       # now we're within an escape code
            continue
        fi
        output+="${char}"                       # if none of the above applies, add current character to output
    done
    eval "$2=\"${output}\""                     # assign output to target variable
}

Voici un exemple correspondant au cas d'utilisation de la question d'origine. Enregistrer sous example.sh puis exécutez <command-producing-colored-output> | example.sh:

#!/bin/bash

# copy&paste function strip_escape_codes here

while read -r line; do
    strip_escape_codes "${line}" stripped
    echo "${stripped}"
done
1
Maxxim