我有一些脚本,产生输出的颜色,我需要删除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?

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


当前回答

我在Debian的有色日志包中遇到了ansi2txt工具。该工具删除来自STDIN的ANSI控制代码。

使用的例子:

./somescript | ansi2txt

源代码http://github.com/kilobyte/colorized-logs

其他回答

我也遇到过类似的问题。我发现的所有解决方案都适用于颜色代码,但没有删除“$(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}

为了正常工作,regex必须扩展以匹配由sgr0 ("\E(B"))添加的序列:

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

我遇到了这个问题/答案,试图做一些类似于op的事情。我找到了一些其他有用的资源,并在此基础上提出了一个日志脚本。在这里发帖,希望能帮助到其他人。

深入研究这些链接有助于理解一些重定向,我不会尝试解释,因为我自己也刚刚开始理解它。

Usage会将彩色输出呈现到控制台,同时将颜色代码从文本中剥离到日志文件。它还将在日志文件中包含任何无效命令的stderr。

编辑:在底部添加更多的使用情况,以显示如何以不同的方式登录

#!/bin/bash
set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

. $DIR/dev.conf
. $DIR/colors.cfg

filename=$(basename ${BASH_SOURCE[0]})
# remove extension
# filename=`echo $filename | grep -oP '.*?(?=\.)'`
filename=`echo $filename | awk -F\. '{print $1}'`
log=$DIR/logs/$filename-$target

if [ -f $log ]; then
  cp $log "$log.bak"
fi

exec 3>&1 4>&2
trap 'exec 2>&4 1>&3' 0 1 2 3
exec 1>$log 2>&1


# log message
log(){
    local m="$@"
    echo -e "*** ${m} ***" >&3
    echo "=================================================================================" >&3
  local r="$@"
    echo "================================================================================="
    echo -e "*** $r ***" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g"
    echo "================================================================================="
}

echo "=================================================================================" >&3
log "${Cyan}The ${Yellow}${COMPOSE_PROJECT_NAME} ${filename} ${Cyan}script has been executed${NC}"
log $(ls) #log $(<command>)

log "${Green}Apply tag to image $source with version $version${NC}"
# log $(exec docker tag $source $target 3>&2) #prints error only to console
# log $(docker tag $source $target 2>&1) #prints error to both but doesn't exit on fail
log $(docker tag $source $target 2>&1) && exit $? #prints error to both AND exits on fail
# docker tag $source $target 2>&1 | tee $log # prints gibberish to log
echo $? # prints 0 because log function was successful
log "${Purple}Push $target to acr${NC}"


以下是其他有用的链接:

我可以使用sed操作一个变量在bash? https://www.cyberciti.biz/faq/redirecting-stderr-to-stdout/ https://unix.stackexchange.com/questions/42728/what-does-31-12-23-do-in-a-script https://serverfault.com/questions/103501/how-can-i-fully-log-all-bash-scripts-actions https://www.gnu.org/software/bash/manual/bash.html#Redirections

下面的正则表达式将错过一些ANSI转义码序列,以及3位颜色。regex101.com的示例和修复。

用这个代替:

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

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

例如,输入echo "$(tput setaf 1)foo$(tput sgr0) bar"

这里有一种方法也可以剥离SI字符(shift in) (0x0f)

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

在纯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
    printf '%s\n' "${line//$'\e'[\[(]*([0-9;])[@-n]/}"
  done
}

See:

扩展Globbing gnu.org: Bash参数扩展

我使用perl,因为我必须经常在许多文件上这样做。这将遍历所有文件名为*.txt的文件,并删除任何格式。这适用于我的用例,可能对其他人也有用,所以只是想在这里发帖。替换文件名*.txt,或者你可以在设置下面的filename变量时用空格分隔文件名。

$ FILENAME=$(ls filename*.txt) ; for file in $(echo $FILENAME); do echo $file; cat $file | perl -pe 's/\e([^\[\]]|\[.*?[a-zA-Z]|\].*?\a)//g' | col -b > $file-new; mv $file-new $file; done