Видалення кольорів з виводу


140

У мене є якийсь сценарій, який дає результат з кольорами, і мені потрібно видалити ANSI-коди.

#!/bin/bash

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

./somescript

Вихід (у файлі журналу):

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

Я не знав, як тут поставити символ ESC, тому я поставив @його місце.

Я змінив сценарій на:

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

Але тепер він дає мені (у файлі журналу):

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

Як я також можу видалити це @[60G?

Можливо, є спосіб повністю відключити забарвлення для всього сценарію?


Для node / npm ви можете використовувати strip-ansi: github.com/chalk/strip-ansi .
Джошуа Пінтер

Відповіді:


165

Згідно з Вікіпедією , команда [m|K]in, sedяку ви використовуєте, спеціально розроблена для обробки m(команда кольору) та K(команда "стерти частину рядка"). Ваш сценарій намагається встановити абсолютне положення курсора на 60 ( ^[[60G), щоб отримати всі ОК у рядку, який ваша sedлінія не охоплює.

(Правильно, [m|K]мабуть , має бути (m|K)або [mK]тому, що ви не намагаєтеся відповідати символу труби. Але це зараз не важливо.)

Якщо ви переключите цей фінальний збіг у вашій команді на [mGK]або (m|G|K), ви повинні мати можливість вловити цю додаткову контрольну послідовність

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

29
Користувачі BSD / OSX: Зазвичай у нас немає варіанту -r. brew install gnu-sedвстановить дієздатну версію. Біжи з gsed.
Nicolai S

1
Якщо я це роблю echo "$(tput setaf 1)foo$(tput sgr0) bar" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | cat -A, я отримую: foo^O bar$Отже, я думаю, деякі символи не видалено правильно, правда? Ви знаєте, як виправити?
edi9999

1
@ edi9999 Наскільки я можу сказати, різниця полягає в тому, що для налаштування кольорів, що перевищують 16 кольорів (як setafпідтримка), потрібно більше параметрів, ніж лише два; мій регекс підтримує два. Зміна першого ?на *допомогу повинна допомогти. Поводження sgr0можливе, але на основі пошуку воно, ймовірно, переростає за межі цієї відповіді, що ґрунтується на виразці.
Джефф Бауман

Гаразд, я додав відповідь, яка додає sed"трубу", щоб зняти "зміну" характеру
edi9999

7
Це не працює надійно, оскільки може бути третє значення (ала [38;5;45m). Ця альтернативна відповідь працює unix.stackexchange.com/a/55547/168277
davemyron

30

Я не міг отримати гідних результатів жодної з інших відповідей, але мені працювало наступне:

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

Якщо я видалив лише керуючий знак "^ [", він залишив решту кольорових даних, наприклад, "33м". Включення кольорового коду та "m" зробили свою справу. Мені спантеличено, що s / \ x1B // g не працює, тому що \ x1B [31m, безумовно, працює з відлунням.


6
На OSX (BSD sed) використовуйте -Eзамість -rрозширеного регулярного вираження. Більше можна знайти тут
Ассамбар

я повинен був замінити {1,3}на {,3}( в іншому випадку він був ще пропускаючи деякі елементи управління), спасибі за ваше рішення!
бездіяльним

6
Оскільки вони можуть бути декількома числами, розділеними напівколонами (для кольору тла, жирного шрифту, курсиву тощо). Ця команда працювала для мене:sed -r "s/[[:cntrl:]]\[([0-9]{1,3};)*[0-9]{1,3}m//g"
saeedgnu

Цей (з багатьох я тестував) працював з вихідним сигналом Ansible, який був запущений з unbuffer.
Мартін

23

ІМХО, більшість цих відповідей намагаються занадто сильно обмежити те, що знаходиться в коді евакуації. Як результат, вони виявляються відсутніми, наприклад, типовими кодами[38;5;60m (ANSI колір 60 в режимі 256 кольорів).

Вони також потребують -rопції, яка дозволяє розширювати GNU . Вони не потрібні; вони просто змушують регулярно читати регулярний вираз.

Ось простіший варіант відповіді, який обробляє 256-кольорові виводи і працює в системах, що не мають GNU sed:

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

Це вловить все, що починається з [, має будь-яку кількість десяткових знаків і крапкових знаків і закінчується буквою. Це повинно зафіксувати будь-яку з загальних послідовностей втечі ANSI .

Для прихильників, ось більш масштабне і загальне (але мінімально перевірене) рішення для всіх можливих послідовностей аварійних викликів ANSI :

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

(і якщо у вас проблема SI @ edi9999, додайте | sed "s/\x0f//g"її до кінця; це працює для будь-якого керуючого символу , замінюючи 0fшестигранну небажану таблицю)


Цей чудово працював, щоб вивести кольоровий рядок із попередньо попередженого виводу Azure Az.
вольвокс

Виправлено @elig. Виявляється, у нього виникло ряд питань, починаючи з того, що якийсь редактор замінив усі мої тире на дивні версії unicode, але й купу неправильних втеч - |у sed, ]всередині класу символів у sed та 'в одноцитованому рядку bash. Зараз він працює для мене надто базовим тестом.
meustrus

20

Для Mac OSX або BSD

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

1
Дивно, що цей відмінно працював для debian, але інші вище не зробили.
cy8g3n

Цей частково спрацював. Однак якщо я відкрию файл у excel, я все ще бачу цього особливого символу "?" в кінці кожного рядка.
doudy_05

@ doudy_05 Спробуйте передати -Eпрапор sed для включення розширеного regexp.
Олександр Зінченко

14

У мене також була проблема, що іноді з'являвся символ СІ.

Це сталося, наприклад, з цим входом: echo "$(tput setaf 1)foo$(tput sgr0) bar"

Ось спосіб також зняти символ SI (зсув) (0x0f)

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

2
Не впевнений, чому ця відповідь отримує так мало кредиту. Це єдиний, хто працює для мене ...
m8mble

8

Хм, не впевнений, що це буде працювати для вас, але 'tr' буде 'знімати' (видаляти) контрольні коди - спробуйте:

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

32
Раптом він також видаляє нові рядки
ruX

Так, LF і CR (коди) - це контрольні коди; якщо вас цікавить декілька рядків, це може бути не рішенням. Оскільки здається, що ви запускаєте програму JAVA, я здогадаюся, що кольорами керують звідти; Інакше вам потрібно буде переглянути налаштування консолі (тобто налаштування термінала / колірну схему) та / або параметри кожної команди, яка підтримує 'кольори', тобто ls --color = ніколи
Dale_Reagan

3
Мені подобається ця відповідь за її елегантність, навіть якщо вона робить більше, ніж просто видалення кольорів. Дякую!
Йоганн Філіп Стратхаузен

7
він насправді пускає коди туди, див. ls -l + ваша команда: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/
До

7

У мене була подібна проблема. Усі знайдені нами рішення добре спрацювали з кольоровими кодами, але не видалили додані символи "$(tput sgr0)"(скидання атрибутів).

Беручи, наприклад, рішення в коментарі від дамейрона, довжина отриманого рядка в наведеному нижче прикладі дорівнює 9, а не 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}

Щоб нормально працювати, регулярний вираз повинен був бути розширений, щоб він також відповідав послідовності, доданій sgr0(" \E(B"):

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

@Jarodiv - дякую за найбільш всеосяжний підхід. Усі відповіді, надані в цій темі, стосуються ТОЛЬКО послідовностей управління ANSI / VT100 (наприклад: "\ e [31mHello World \ e [0m"), однак не усувають нічого, викликаного форматуванням тексту TPUT (наприклад: tput smso / tput setaf X / tput rmso / tput sgr0). В результаті після всіх "sed" страт залишився якийсь інший безлад у журналах. Це чисте рішення для моїх шаф!
безликий

5

Набагато простіша функція в чистому Bash відфільтрувати загальні коди ANSI з текстового потоку:

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

Побачити:

  1. linuxjournal.com: Розширена глобалізація
  2. gnu.org: Розширення параметра Bash

1
Це не працює. Тест с tldr. (Хоча я використовую zsh, тому це може бути і через це.)
HappyFace

Дійсно, Zsh не зрозуміє розширеного глобусу Баша extglobабо, мабуть, і зовсім не зрозуміє заміну рядків.
Léa Gris

Я ввімкнув розширений глобус zsh ... Заміна рядка повинна бути також posix?
HappyFace

Заміна рядків не є POSIX. Ви можете використовувати будь-який із альтернативних методів, використовуючи sedзгадані тут, які працюватимуть із Zsh.
Léa Gris

Це рішення має перевагу буферизації тексту. Я спробував з sed, але це блокувало мою трубу.
Гільєрмо

3

@ рішення Джеффа-Боумана допомогло мені позбутися ЯКЩО кольорових кодів. Я додав ще одну невелику частину в регулярний вираз, щоб видалити ще кілька:

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

Ось чисте рішення Баша.

Збережіть як strip-escape-codes.sh, зробіть виконуваний файл і запустіть <command-producing-colorful-output> | ./strip-escape-codes.sh.

Зауважте, що це позбавляє всіх кодів / послідовностей аварійних програм ANSI. Якщо ви хочете лише смугасті кольори, замініть [a-zA-Z]на "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

Ну, це рішення могло бути ще менш складним.
Олександр Зінченко

1

Суперечлива ідея полягала б у тому, щоб перенастроїти параметри терміналу для цього процесного середовища, щоб процес знав, що термінал не підтримує кольори.

Щось подібне TERM=xterm-mono ./somescriptприходить мені в голову. YMMV з вашою конкретною ОС і здатністю вашого сценарію розуміти налаштування кольорів терміналу.


-7

Це працює для мене:

./somescript | cat

3
Це залежить від способу somescriptреалізації. Він може або не може визнати, що його стандартний вихід є tty. (Слова правопорушників насправді в програмі коди аварійних програм, що стосуються жорсткого коду, і жахливо ламаються при використанні на інших терміналах або в сценаріях).
Toby Speight

Дякую Тобі. Я використовував django manager.py для тестування, але те, що ви сказали, має сенс.
spiderlama
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.