Удаление цветов из вывода

141

У меня есть сценарий, который производит вывод с цветами, и мне нужно удалить коды 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?

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

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

Ответы:

166

Согласно Википедии , [m|K]используемая sedвами команда специально предназначена для обработки m(команда color) и K(команда «erase part of line»). Ваш скрипт пытается установить абсолютную позицию курсора в 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 для sed. brew install gnu-sedустановит способную версию. Беги с gsed.
Николай С
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к трубе, чтобы удалить символ "shift in"
edi9999
7
Это не работает надежно, так как может быть третье значение (аля [38;5;45m). Этот альтернативный ответ работает unix.stackexchange.com/a/55547/168277
davemyron
30

Я не смог получить приличные результаты ни от одного из других ответов, но мне помогло следующее:

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

Если я удалил только контрольный символ «^ [», он оставил остальные данные цвета, например, «33m». В том числе цветовой код и «м» сделали свое дело. Я озадачен тем, что s / \ x1B // g не работает, потому что \ x1B [31m определенно работает с echo.

JoeAndrieu
источник
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, который запускался с небуфером.
Мартин
23

ИМХО, большинство из этих ответов слишком стараются ограничить то, что находится внутри escape-кода. В результате они теряют общие коды, такие как [38;5;60m(основной цвет ANSI 60 из 256-цветного режима).

Им также требуется -rопция, которая включает расширения GNU . Это не обязательно; они просто делают регулярное выражение лучше.

Вот более простой ответ, который обрабатывает переходы с 256 цветами и работает в системах с не-GNU sed:

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

Это поймает все, что начинается с [, имеет любое количество десятичных знаков и точек с запятой и заканчивается буквой. Это должно поймать любую из общих escape-последовательностей ANSI .

Для фанатов вот более масштабное и более общее (но минимально проверенное) решение для всех возможных выходных последовательностей ANSI :

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

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

meustrus
источник
Это прекрасно работало для выделения цвета из вывода Azure az, предварительно оптимизированного.
volvox
Исправлено @elig. Оказывается, у него был ряд проблем, начиная с того, что какой-то редактор заменил все мои черточки странными версиями юникода, но также и с кучей неправильных экранировок - |в sed, ]внутри класса символов в sed и 'в строке bash в одинарных кавычках. Сейчас он работает для меня на очень простой тестовый пример.
meustrus
20

Для Mac OSX или BSD использовать

./somescript | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g'
grebulon
источник
1
Странно, этот работает нормально для Debian, а другие выше - нет.
cy8g3n
Этот частично работал. Тем не менее, если я открою файл в Excel, я все еще вижу этот специальный символ "?" в конце каждой строки.
doudy_05
@ doudy_05 Попробуйте передать -Eфлаг для sed, чтобы включить расширенное регулярное выражение.
Александр Зинченко
14

У меня также была проблема, что иногда появлялся символ СИ.

Это случилось, например, с этим входом: echo "$(tput setaf 1)foo$(tput sgr0) bar"

Вот способ также удалить символ СИ (сдвиг) (0x0f)

./somescript | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | sed "s/\x0f//g"
edi9999
источник
2
Не уверен, почему этот ответ получает так мало кредитов. Это единственный, кто работает на меня ...
m8mble
8

Хм, не уверен, что это будет работать для вас, но 'tr' удалит (удалит) контрольные коды - попробуйте:

./somescript | tr -d '[:cntrl:]'
Dale_Reagan
источник
32
Внезапно он также удаляет новые строки
ruX
Да, LF и CR (коды) являются контрольными кодами; если вы заинтересованы в более чем одной строке, то это не может быть решением. Так как кажется, что вы работаете с JAVA-программой, я думаю, что цвета управляются оттуда; В противном случае вам нужно будет посмотреть настройки вашей консоли (то есть настройки терминала / цветовую схему) и / или параметры для каждой команды, которая поддерживает «цвета», т.е. ls --color = never
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)"(сброс атрибутов).

Взяв, к примеру, решение в комментарии davemyron, длина полученной строки в приведенном ниже примере равна 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
источник
@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 не поймет расширенного сглаживания Bash extglobили, вероятно, не поймет заменители строк вообще.
Леа Гри
Я включил extendedglob в zsh ... Замена строки тоже должна быть posix?
HappyFace
Замена строки не POSIX. Вы можете использовать любой из альтернативных методов, sedупомянутых здесь, которые будут работать с Zsh.
Леа Гри
Это решение имеет преимущество буферизации текста. Я попытался с помощью Sed, но это была буферизация блоков моей трубы.
Гильермо
3

Решение @ jeff-bowman помогло мне избавиться от НЕКОТОРЫХ цветовых кодов. Я добавил еще одну небольшую часть в регулярное выражение, чтобы удалить еще:

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
источник
2

Вот чистое решение Bash.

Сохраните как strip-escape-codes.sh, сделайте исполняемый файл и затем запустите <command-producing-colorful-output> | ./strip-escape-codes.sh.

Обратите внимание, что это удаляет все escape-коды / последовательности 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

Баш <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
источник
Ну, это решение может быть еще менее сложным.
Александр Зинченко
1

Спорная идея будет перенастроить настройки терминала для этого окружение процесса, чтобы процесс знает, что терминал не поддерживает цвета.

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

AB
источник
-7

Это работает для меня:

./somescript | cat
spiderlama
источник
3
Это зависит от того, как somescriptэто реализовано. Он может или не может признать, что его стандартный вывод является tty. (Слова «оскорбители» на самом деле жестко зашифровывают специфические для терминала коды перехода в программу и ужасно ломаются при использовании на других терминалах или в сценариях).
Тоби Спейт
Спасибо Тоби. Я использовал django manage.py для тестирования, но то, что вы сказали, имеет смысл.
спайдерлама