The Gist
由于终端着色的工作方式,再加上 bash 跟踪的工作方式,实现起来相当难看,但实现方法如下:
首先,您需要找到一些既不会出现在跟踪中、也不会出现在 stdout 中、也不会出现在 stderr 中的唯一字符串。在我的例子中,它是@@@
.
然后,如果您的脚本被调用example.sh
, 你跑
$ ./color_trace.sh example.sh
Where ./color_trace.sh
是这个脚本:
#!/bin/bash
RED=1
UNIQUE_STR='@@@'
COLOR=$RED
normal()
{
tput sgr0 # tell the terminal to not style the output
}
colored()
{
tput setaf $COLOR # tell the terminal to set forward color to $COLOR
}
export PS4="+${UNIQUE_STR}" # artificially insert $UNIQUE_STR to the trace
exec &> >(sed "s/\(\+\)*\+\(${UNIQUE_STR}\)\(.*\)$/$(colored)\1+\3$(normal)/") # see below
set -x # set trace
source "$@" # run the commands in the current shell so that all settings apply
这将为跟踪命令着色red https://linux.101hacks.com/ps1-examples/prompt-color-using-tput/.
解释
它的实现方式如下:
- Insert
$UNIQUE_STR
到输出
- 通过命令路由所有输出 - stdout 和 stderr (
exec &> >(...)
)
- 命令 (
sed
) 假设轨迹的形式为+++${UNIQUE_STR} <some interesting trace>
- 查找带有以下内容的行
$UNIQUE_STR
(s/...\(${UNIQUE_STR}\).../
)并将其删除(/...\1+\3.../
, where \1
is ++
and \3
is <some interesting trace>
)
- 如果该行确实包含
$UNIQUE_STR
,告诉终端在该行之前将颜色设置为红色,并在该行之后取消设置颜色(/$(colored)...$(normal)/
).
什么不起作用
一、功能colored()
and normal()
不接受一段文字要着色。相反,它们指示终端将颜色设置为接下来要写入的内容,并在写入后指示它取消设置颜色。这就是着色或任何类型的样式在 Unix 终端中的工作方式。
其次,跟踪转到 stderr,因此它将与 stderr 的其他输出混合。这可以改变使用BASH_XTRACEFD https://askubuntu.com/questions/811439/bash-set-x-logs-to-file,但在这里效果不佳。
由于您需要着色,因此假定您希望将跟踪与其他输出区分开来,因此假定所有输出(包括 stdout、stderr 和跟踪)都将发送到终端。
如果所有内容都发送到终端,那么为了使跟踪与实际输出正确交错,所有输出(stdout、stderr 和跟踪)都需要在同一文件描述符上执行。这就是为什么我们必须通过以下方式重定向一切sed
,不仅仅是痕迹。如果我们只重定向跟踪,那么您将获得来自各种命令的一堆输出,只有在之后您才会看到这些命令的所有跟踪,例如
... actual output from /etc/passwd...
... actual output from /etc/group...
+source ./script.sh
++cat_file /etc/passwd
++cat /etc/passwd
++cat_file /etc/group
++cat /etc/group
如果不是因为文件描述符问题,我们就不需要UNIQUE_STR
,我们可以使用类似的东西:
exec 19> >(sed "s/^.*$/$(colored)&$(normal)/")
BASH_XTRACEFD=19
set -x
source "$@"
值得注意的是,即使在这个版本中,我们也必须打开和关闭每行的颜色,因为“实际输出”的行需要不着色。