覆盖行的脚本的着色输出?

Coloring output of a script that overwrites lines?

我正在使用它为 script/command:

的输出着色
commandWithOutput | sed -r 's/(pattern)/'"${COLOR_RED}"''"${COLOR_DEFAULT}"'/g'

(这将为命令输出中出现的所有字符串“pattern”涂上颜色。)它与传统命令配合得很好。但是,如果 script/command 覆盖其输出中的行(也许这与 terminal/console 的关系不仅仅是标准输出?),例如:

Building project X:
CXX Building file XYZ.cpp... [123/1034]

行为不符合预期。我的 sed 仍然会为输出着色,但覆盖不再起作用,即:

Building project X:
CXX Building file ABC.cpp... [1/1034]
CXX Building file DEF.cpp... [2/1034]
CXX Building file GHI.cpp... [3/1034]
CXX Building file JKL.cpp... [4/1034]
CXX Building file MNO.cpp... [5/1034]
// and so on...
CXX Building file XYZ.cpp... [123/1034]

有没有办法为覆盖行的 script/command 的输出着色?

我尝试了几种不同的想法...IFS=$'\r' + OP 的 sed 命令...尝试使用中间管道 (mkfifo) 处理来自 commandWithOutput ...尝试取消缓冲 stdout and/or stdin ...但(到目前为止)只能得到 awk 解决方案,所以 fwiw ...

注意:我假设 OP 的命令在覆盖一行时生成 \r;如果不是这种情况,OP 可以尝试将命令的输出通过管道传输到 | od -c 以查看 'end of the line' 处的字符是什么,其想法是使用所述字符代替我的 \r参考文献(下)。


首先我们将编写一个小脚本来生成一些数据,(重新)打印前几行,然后打印一些 'standalone' 行:

$ cat overwrite
#!/usr/bin/bash

for (( i=1 ; i<="" ; i++ ))
do
    printf "this is a test ... ${i}\r"
    sleep 1
done

printf "\nanother test output \t and a tab\n"

echo "X."

运行 以上生成以下输出:

$ overwrite 3
this is a test ... 3                      << this line is actually printed 3x times with suffixes of '1', '2' and '3'
another test output      and a tab
X.

运行 这通过 od 显示前 3 行末尾的 \r

$ overwrite 3 | od -c
0000000   t   h   i   s       i   s       a       t   e   s   t       .
0000020   .   .       1  \r   t   h   i   s       i   s       a       t
0000040   e   s   t       .   .   .       2  \r   t   h   i   s       i
0000060   s       a       t   e   s   t       .   .   .       3  \r  \n
0000100   a   n   o   t   h   e   r       t   e   s   t       o   u   t
0000120   p   u   t      \t       a   n   d       a       t   a   b  \n
0000140   X   .  \n
0000143

我们现在来看一个 awk 解决方案,用于重新着色我们 overwrite 脚本输出中的特定模式 ...

首先,我们将为所需的颜色定义开始和 clear/reset 变量;对于这个练习,我将使用 'red':

$ myred=$(tput setaf 1)           # set our highlight color to red
$ myreset=$(tput sgr0)            # disable coloring

注意:有几种方法可以定义这些颜色(以及 disable/reset);我会把这个留给 reader 来选择最适合他们环境的东西。

这是我发现的一个 awk 解决方案:

$ overwrite 3 | awk -v ptn="test" -v cstart="${myred}" -v creset="${myreset}" -v RS="[\n\r]" '{ sub(ptn,cstart ptn creset) ; printf [=14=] RT }'

其中:

  • -v ptn="test" - 我们想要重新着色字符串 test 的所有实例;我们将把它作为 awk 变量 ptn
  • 传递
  • -v cstart="${myred}" - 将我们的高亮颜色代码(红色)分配给我们的 awk 变量 cstart
  • -v creset="${myreset}" - 将我们的颜色 clear/reset 代码分配给 awk 变量 creset
  • -v RS="[\n\r]" - 将我们的输入记录分隔符重新定义为 \r\n
  • sub(ptn,cstart ptn creset) - 将 test 的所有实例替换为 <red> + test + <reset>
  • printf [=45=] RT - 打印我们的新行; RT 允许我们使用用于解析此记录的相同 RS

运行 以上生成:

this is a test ... 3                      << this line is actually printed 3x times with suffixes of '1', '2' and '3', and the 'test' string printed in red
another test output      and a tab        << the 'test' string is printed in red
X.