如何将彩色文本输出到标准输出,同时将未着色的文本附加到文件中?
How to output colored text to stdout while also appending the text uncolored to a file?
我写了一个记录器函数,它应该将彩色文本输出到屏幕,同时使用 tee
命令将文本重定向到日志文件。
这是记录器函数:
function logger(){
GREEN=$(tput setaf 2)
YELLOW=$(tput setaf 3)
BOLD=$(tput bold)
UNDERLINE=$(tput smul)
NOCOLOR=$(tput sgr0)
case "" in
y)
echo -e -n "$(timestamp) ${YELLOW} ${NOCOLOR}\n" | tee -a $log_file
;;
g)
echo -e -n "$(timestamp) ${GREEN} ${NOCOLOR}\n" | tee -a $log_file
;;
b)
echo -e -n "$(timestamp) ${BOLD} ${NOCOLOR}\n" | tee -a $log_file
;;
u)
echo -e -n "$(timestamp) ${UNDERLINE} ${NOCOLOR}\n" | tee -a $log_file
;;
n)
echo -e -n "$(timestamp) \n" | tee -a $log_file
;;
*)
echo "Unknown color!"
;;
esac
}
问题是在日志文件中写入文本时,还会应用颜色代码,这会使文本看起来很脏。
这是它的样子:
2021-08-31 12:36:41 UTC +0000 ^[[1m------------ Now working on account CompanyEUResearchAndDevelopmentJenkinsSlave in region us-east-2 ^[(B^[[m
2021-08-31 12:36:42 UTC +0000 ^[[32mThe following IPs are allocated and associated: ^[(B^[[m
2021-08-31 12:36:42 UTC +0000 13.11.131.202 18.11.34.219 3.11.227.231
2021-08-31 12:36:42 UTC +0000 ----------------------------------------------------------
2021-08-31 12:36:44 UTC +0000 ^[[33mFound unassociated elastic ips: ^[(B^[[m
2021-08-31 12:36:44 UTC +0000 18.11.91.21
2021-08-31 12:36:45 UTC +0000 ^[[33mIP 18.11.91.21 found in groups: ^[(B^[[m
2021-08-31 12:36:45 UTC +0000 sg-04b6da1d06783ffbf
2021-08-31 12:36:46 UTC +0000 ^[[32mThe rule containing ip 18.11.91.21 has been deleted from security group sg-04b6da1d06783ffbf successfully ^[(B^[[m
2021-08-31 12:36:46 UTC +0000 ^[[32mReleasing ip 18.11.91.21 with AllocationId eipalloc-07528d88e8794c6db ^[(B^[[m
2021-08-31 12:36:46 UTC +0000 ^[[32mAllocation released successfully! ^[(B^[[m
2021-08-31 12:36:47 UTC +0000 ^[[33mFound unassociated elastic ips: ^[(B^[[m
2021-08-31 12:36:47 UTC +0000 3.11.248.103
2021-08-31 12:36:48 UTC +0000 ^[[33mIP 3.11.248.103 found in groups: ^[(B^[[m
2021-08-31 12:36:48 UTC +0000 sg-04b6da1d06783ffbf
2021-08-31 12:36:49 UTC +0000 ^[[32mThe rule containing ip 3.11.248.103 has been deleted from security group sg-04b6da1d06783ffbf successfully ^[(B^[[m
2021-08-31 12:36:49 UTC +0000 ^[[32mReleasing ip 3.11.248.103 with AllocationId eipalloc-0a699e32f4ac844dc ^[(B^[[m
2021-08-31 12:36:49 UTC +0000 ^[[32mAllocation released successfully! ^[(B^[[m
2021-08-31 12:36:49 UTC +0000 End of run
函数的使用方法:
logger g "No elastic ips found in account ${role_name}"
我想知道“最正确”的方法是什么?
通过命令 ansi2txt
.
在 tee
之前尝试管道
例如:
echo -e -n "$(timestamp) ${UNDERLINE} ${NOCOLOR}\n" | ansi2txt | tee -a $log_file
对 Vaphell's answer 的修改(朝向页面底部),从 appending/writing 到 $log_file
之前的 tee
流中删除了颜色代码:
改变这个:
echo -e -n "$(timestamp) ${GREEN} ${NOCOLOR}\n" | tee -a $log_file
为此:
echo -e -n "$(timestamp) ${GREEN} ${NOCOLOR}\n" | tee >(sed 's/\x1B[\[\(][0-9;]*[BJKmsu]//g' >> $log_file)
由于您可能希望经常调用 logger()
,因此性能将成为一个大问题,而最大的改进将来自尽可能多地消除(不必要的)子进程调用。
tl;dr
- 使用 2 次单独的
echo
调用,一次到标准输出,一次到 $log_file
- 消除我假设的子进程
$(timestamp)
调用包括对 date
的进一步子进程调用
- 仅加载一次颜色变量(即
tput
子进程调用),即消除每次调用 logger()
[= 时重复的 5 次子进程调用(对 tput
) 109=]
- 有关更多详细信息,请继续阅读 ...
使用 2 次单独的 echo
调用
根据 user1934428 的评论...调用 echo
两次,一次到标准输出,一次到(附加到)$log_file
是另一种选择,例如:
而不是这个:
echo -e -n "$(timestamp) ${GREEN} ${NOCOLOR}\n" | tee >(sed 's/\x1B[\[\(][0-9;]*[BJKmsu]//g' >> $log_file)
使用这个:
echo -e -n "$(timestamp) ${GREEN} ${NOCOLOR}\n"
echo -e -n "\n" >> $log_file
使用以下测试:
time for i in {1..100}
do
mylogger g hello > /dev/null
done
备注:
- 在我的系统上有一个
/usr/bin/logger
所以我将我的函数命名为 mylogger()
- 我修改了我的 env 只加载一次颜色变量,而不是 OP 的当前方法每次调用函数时进行 5 次
tput
调用(否则以下测试的时间将是均匀的更糟)
结果:
real 0m8.739s # echo ... | tee >(sed ... >> $log_file)
user 0m3.448s
sys 0m4.706s
real 0m0.105s # echo ... ; echo ...
user 0m0.047s
sys 0m0.047s
如您所见,使用两个单独的 echo
命令将比 | tee >(sed ...)
解决方案快 LOT ... 或者任何其他需要进行(不必要的)子流程调用的解决方案
消除子进程$(timestamp)
(和date
)调用
我假设 $(timestamp)
调用是一个自定义 function/binary,它从当前 date
生成一个 date/time 字符串;如果 运行ning bash 4.2
(或更好)考虑使用 printf
生成 date/time 字符串并存储在局部变量中,例如:
printf -v NOW '%(%F %H:%M:%S %Z %z)T' -1
备注:
- 这不需要对
date
的子进程调用,因此 运行 会快很多
- 有关详细信息,请参阅 this SE answer
- 一般的想法是直接在
logger()
函数中进行 printf
调用,从而消除 $(timestamp)
子进程调用
只加载一次颜色变量(即,tput
子进程调用)
正如我在我的一条评论中提到的,考虑一次填充颜色变量,从而消除每次调用 logger()
函数时对 tput
进行 5 次子进程调用的当前过程;一个非常简单的解决方案,继续使用 OP 当前的 <color>=$(tput ...)
格式:
function load_colors() {
if [[ -z "${NOCOLOR}" ]]
then
export GREEN=$(tput setaf 2)
export YELLOW=$(tput setaf 3)
export BOLD=$(tput bold)
export UNDERLINE=$(tput smul)
export NOCOLOR=$(tput sgr0)
fi
}
function logger() {
load_colors
case "" in
... snip ...
esac
}
我写了一个记录器函数,它应该将彩色文本输出到屏幕,同时使用 tee
命令将文本重定向到日志文件。
这是记录器函数:
function logger(){
GREEN=$(tput setaf 2)
YELLOW=$(tput setaf 3)
BOLD=$(tput bold)
UNDERLINE=$(tput smul)
NOCOLOR=$(tput sgr0)
case "" in
y)
echo -e -n "$(timestamp) ${YELLOW} ${NOCOLOR}\n" | tee -a $log_file
;;
g)
echo -e -n "$(timestamp) ${GREEN} ${NOCOLOR}\n" | tee -a $log_file
;;
b)
echo -e -n "$(timestamp) ${BOLD} ${NOCOLOR}\n" | tee -a $log_file
;;
u)
echo -e -n "$(timestamp) ${UNDERLINE} ${NOCOLOR}\n" | tee -a $log_file
;;
n)
echo -e -n "$(timestamp) \n" | tee -a $log_file
;;
*)
echo "Unknown color!"
;;
esac
}
问题是在日志文件中写入文本时,还会应用颜色代码,这会使文本看起来很脏。
这是它的样子:
2021-08-31 12:36:41 UTC +0000 ^[[1m------------ Now working on account CompanyEUResearchAndDevelopmentJenkinsSlave in region us-east-2 ^[(B^[[m
2021-08-31 12:36:42 UTC +0000 ^[[32mThe following IPs are allocated and associated: ^[(B^[[m
2021-08-31 12:36:42 UTC +0000 13.11.131.202 18.11.34.219 3.11.227.231
2021-08-31 12:36:42 UTC +0000 ----------------------------------------------------------
2021-08-31 12:36:44 UTC +0000 ^[[33mFound unassociated elastic ips: ^[(B^[[m
2021-08-31 12:36:44 UTC +0000 18.11.91.21
2021-08-31 12:36:45 UTC +0000 ^[[33mIP 18.11.91.21 found in groups: ^[(B^[[m
2021-08-31 12:36:45 UTC +0000 sg-04b6da1d06783ffbf
2021-08-31 12:36:46 UTC +0000 ^[[32mThe rule containing ip 18.11.91.21 has been deleted from security group sg-04b6da1d06783ffbf successfully ^[(B^[[m
2021-08-31 12:36:46 UTC +0000 ^[[32mReleasing ip 18.11.91.21 with AllocationId eipalloc-07528d88e8794c6db ^[(B^[[m
2021-08-31 12:36:46 UTC +0000 ^[[32mAllocation released successfully! ^[(B^[[m
2021-08-31 12:36:47 UTC +0000 ^[[33mFound unassociated elastic ips: ^[(B^[[m
2021-08-31 12:36:47 UTC +0000 3.11.248.103
2021-08-31 12:36:48 UTC +0000 ^[[33mIP 3.11.248.103 found in groups: ^[(B^[[m
2021-08-31 12:36:48 UTC +0000 sg-04b6da1d06783ffbf
2021-08-31 12:36:49 UTC +0000 ^[[32mThe rule containing ip 3.11.248.103 has been deleted from security group sg-04b6da1d06783ffbf successfully ^[(B^[[m
2021-08-31 12:36:49 UTC +0000 ^[[32mReleasing ip 3.11.248.103 with AllocationId eipalloc-0a699e32f4ac844dc ^[(B^[[m
2021-08-31 12:36:49 UTC +0000 ^[[32mAllocation released successfully! ^[(B^[[m
2021-08-31 12:36:49 UTC +0000 End of run
函数的使用方法:
logger g "No elastic ips found in account ${role_name}"
我想知道“最正确”的方法是什么?
通过命令 ansi2txt
.
tee
之前尝试管道
例如:
echo -e -n "$(timestamp) ${UNDERLINE} ${NOCOLOR}\n" | ansi2txt | tee -a $log_file
对 Vaphell's answer 的修改(朝向页面底部),从 appending/writing 到 $log_file
之前的 tee
流中删除了颜色代码:
改变这个:
echo -e -n "$(timestamp) ${GREEN} ${NOCOLOR}\n" | tee -a $log_file
为此:
echo -e -n "$(timestamp) ${GREEN} ${NOCOLOR}\n" | tee >(sed 's/\x1B[\[\(][0-9;]*[BJKmsu]//g' >> $log_file)
由于您可能希望经常调用 logger()
,因此性能将成为一个大问题,而最大的改进将来自尽可能多地消除(不必要的)子进程调用。
tl;dr
- 使用 2 次单独的
echo
调用,一次到标准输出,一次到$log_file
- 消除我假设的子进程
$(timestamp)
调用包括对date
的进一步子进程调用
- 仅加载一次颜色变量(即
tput
子进程调用),即消除每次调用logger()
[= 时重复的 5 次子进程调用(对tput
) 109=] - 有关更多详细信息,请继续阅读 ...
使用 2 次单独的 echo
调用
根据 user1934428 的评论...调用 echo
两次,一次到标准输出,一次到(附加到)$log_file
是另一种选择,例如:
而不是这个:
echo -e -n "$(timestamp) ${GREEN} ${NOCOLOR}\n" | tee >(sed 's/\x1B[\[\(][0-9;]*[BJKmsu]//g' >> $log_file)
使用这个:
echo -e -n "$(timestamp) ${GREEN} ${NOCOLOR}\n"
echo -e -n "\n" >> $log_file
使用以下测试:
time for i in {1..100}
do
mylogger g hello > /dev/null
done
备注:
- 在我的系统上有一个
/usr/bin/logger
所以我将我的函数命名为mylogger()
- 我修改了我的 env 只加载一次颜色变量,而不是 OP 的当前方法每次调用函数时进行 5 次
tput
调用(否则以下测试的时间将是均匀的更糟)
结果:
real 0m8.739s # echo ... | tee >(sed ... >> $log_file)
user 0m3.448s
sys 0m4.706s
real 0m0.105s # echo ... ; echo ...
user 0m0.047s
sys 0m0.047s
如您所见,使用两个单独的 echo
命令将比 | tee >(sed ...)
解决方案快 LOT ... 或者任何其他需要进行(不必要的)子流程调用的解决方案
消除子进程$(timestamp)
(和date
)调用
我假设 $(timestamp)
调用是一个自定义 function/binary,它从当前 date
生成一个 date/time 字符串;如果 运行ning bash 4.2
(或更好)考虑使用 printf
生成 date/time 字符串并存储在局部变量中,例如:
printf -v NOW '%(%F %H:%M:%S %Z %z)T' -1
备注:
- 这不需要对
date
的子进程调用,因此 运行 会快很多 - 有关详细信息,请参阅 this SE answer
- 一般的想法是直接在
logger()
函数中进行printf
调用,从而消除$(timestamp)
子进程调用
只加载一次颜色变量(即,tput
子进程调用)
正如我在我的一条评论中提到的,考虑一次填充颜色变量,从而消除每次调用 logger()
函数时对 tput
进行 5 次子进程调用的当前过程;一个非常简单的解决方案,继续使用 OP 当前的 <color>=$(tput ...)
格式:
function load_colors() {
if [[ -z "${NOCOLOR}" ]]
then
export GREEN=$(tput setaf 2)
export YELLOW=$(tput setaf 3)
export BOLD=$(tput bold)
export UNDERLINE=$(tput smul)
export NOCOLOR=$(tput sgr0)
fi
}
function logger() {
load_colors
case "" in
... snip ...
esac
}