Bash 从输出中删除颜色

Bash 从输出中删除颜色,bash,unix,colors,console,ansi-escape,Bash,Unix,Colors,Console,Ansi Escape,我有一些脚本可以生成带有颜色的输出,我需要删除ANSI代码 #!/bin/bash exec > >(tee log) # redirect the output to a file but keep it on stdout exec 2>&1 ./somescript 输出为(在日志文件中): 我不知道如何将ESC字符放在这里,所以我将@放在它的位置 我将脚本更改为: #!/bin/bash exec > >(tee log) # red

我有一些脚本可以生成带有颜色的输出,我需要删除ANSI代码

#!/bin/bash

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

./somescript
输出为(在日志文件中):

我不知道如何将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"
但现在它给了我(在日志文件中):

如何删除此“
@[60G

也许有一种方法可以完全禁用整个脚本的着色功能?

,您正在使用的
sed
命令中的
[m|K]
专门用于处理
m
(颜色命令)和
K
(删除行的一部分)命令。您的脚本正在尝试将绝对光标位置设置为60(
^[[60G
)将所有OK放在一行中,这是您的
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"

嗯,不确定这是否适用于您,但'tr'将'strip'(删除)控制代码-尝试:

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

下面的正则表达式将丢失一些序列,以及3位颜色。和在regex101.com上

改用这个:

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

我也有一个问题,有时,SI字符出现

例如,它发生在以下输入中:
echo“$(tput setaf 1)foo$(tput sgr0)bar”

这里还有一种剥离SI字符(移入)(0x0f)的方法


@杰夫·鲍曼的解决方案帮助我摆脱了一些色码。 我在正则表达式中添加了另一小部分,以便删除更多:

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)
这对我很有用:

./somescript | cat

我无法从其他任何一个答案中得到令人满意的结果,但以下几点对我很有用:

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

如果我只删除了控制字符“^[”,它会留下其余的颜色数据,例如“33m”。包括颜色代码和“m”就完成了这项任务。我对s/\x1B//g不起作用感到困惑,因为\x1B[31m肯定与echo一起工作。

这里有一个纯Bash解决方案

另存为
strip escape codes.sh
,使其可执行,然后运行
|。/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
!/usr/bin/env bash
#带ANSI转义码/序列[$1:输入字符串,$2:目标变量]
功能条\转义\代码(){
本地输入=“$1”\u i\u字符\u转义=0
本地-n_output=“$2”_output=“”
对于(_i=0;_i<${#u input};_i++),请执行以下操作
_char=“${u输入:{u i:1}”
如果(${u escape}==1));那么
如果[[“${{u char}”==[a-zA-Z]];那么
_逃逸=0
fi
持续
fi
如果[[“${u char}”==$'\e']];则
_逃逸=1
持续
fi
_输出+=“${u char}”
完成
}
当读取-r行时;执行
strip_转义码“${line}”行
回显“${line_}”
完成
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
!/usr/bin/env bash
#带ANSI转义码/序列[$1:输入字符串,$2:目标变量]
功能条\转义\代码(){
本地输入=“${1/\”/\\\“}”输出=“”i char escape=0
对于((i=0;i<${#input};++i)),do#处理输入字符串的所有字符
char=“${input:i:1}”#从输入字符串中获取当前字符
如果(${escape}==1));那么#如果我们当前在一个转义序列中,请检查
如果[[“${char}”==[a-zA-Z]];则到达#结尾,即如果当前字符是字母
逃逸=0#到达终点,我们不再处于逃逸序列中
fi
继续#跳过当前字符,即不添加到输出
fi
如果[[“${char}”==$'\e']];那么#如果当前字符是'\e',我们已经到达开始位置
转义=转义序列的1#->设置标志
继续#跳过当前字符,即不添加到输出
fi
输出+=“${char}”#将当前字符添加到输出
完成
eval“$2=\”${output}\”\将输出分配给目标变量
}
当读取-r行时;执行
strip_转义码“${line}”行
回显“${line_}”
完成

IMHO,这些答案中的大多数都试图限制转义码内的内容。因此,它们最终会丢失常见代码,如
[38;5;60m
(256色模式下的前景ANSI颜色60)

它们还需要启用的
-r
选项。这些选项不是必需的;它们只是使正则表达式的可读性更好

下面是一个更简单的答案,用于处理256色转义,并适用于具有非GNU
sed
的系统:

./somescript | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g'
这将捕获以
[
开头、有任意数量的小数和分号、以字母结尾的任何内容。这将捕获以下任何内容

对于趣味,这里有一个更大、更通用(但测试最少)的解决方案:

/somescript | sed's/\x1B[@A-Z\\\]^\x1B\[[0-9:;?]*[-!“\$%&'””()*+,.\/]*[\@A-Z^ A-Z{A-Z}]///g'

(如果您有@edi9999的SI问题,请在末尾添加
|sed“s/\x0f//g”
;这可以通过将
0f
替换为不需要的字符的十六进制来实现)

我也有类似的问题。我找到的所有解决方案都适用于颜色代码,但没有删除
“$(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}
适用于Mac OSX或BS
#!/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
./somescript | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g'
./somescript | sed 's/\x1B[@A-Z\\\]^_]\|\x1B\[[0-9:;<=>?]*[-!"#$%&'"'"'()*+,.\/]*[][\\@A-Z^_`a-z{|}~]//g'
#!/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}
string_sed="$( sed -r "s/\x1B(\[[0-9;]*[JKmsu]|\(B)//g" <<< "${string}" )"
./somescript | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g'
# 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
}
TERM=dumb ./somescript 
TERM=dumb tput sgr0 | xxd
tput sgr0 | xxd
00000000: 1b28 421b 5b6d                           .(B.[m

./somescript | ansi2txt