Bash 彩色xtrace输出

Bash 彩色xtrace输出,bash,shell,Bash,Shell,我有一个.sh脚本,它使用set-o xtrace打印以下所有命令。 我想给这些命令上色。我尝试像这样使用PS4变量: export PS4='\[\e[36m\]\+ \[\e[m\]' 但是这只会给+-字符上色,如果我省略了\[\e[m\]我的完整输出,执行程序的输出会上色。 打印命令后是否附加了另一个变量,我可以在其中重置颜色,或者是否有其他方法。 谢谢。我正在使用PS1 var为整个终端着色,下面是我的示例,以便完全清楚哪个颜色是什么(以及在文件夹名称之前添加git分支): 从manb

我有一个.sh脚本,它使用
set-o xtrace
打印以下所有命令。 我想给这些命令上色。我尝试像这样使用PS4变量:

export PS4='\[\e[36m\]\+ \[\e[m\]'
但是这只会给
+
-字符上色,如果我省略了
\[\e[m\]
我的完整输出,执行程序的输出会上色。 打印命令后是否附加了另一个变量,我可以在其中重置颜色,或者是否有其他方法。
谢谢。

我正在使用PS1 var为整个终端着色,下面是我的示例,以便完全清楚哪个颜色是什么(以及在文件夹名称之前添加git分支):

manbash

PS4
此参数的值与PS1一样展开,并打印该值 在执行跟踪期间显示每个命令之前 PS4的扩展值的字符被复制多次(必要时),以指示多个间接级别。默认值为“+”

几乎可以让我们做您需要的;
PS0='\e[0m'
在每个命令运行之前向终端写入重置代码,但不幸的是,它是在打印
PS4
之前而不是之后打印的,所以这不好。正如其他人所说,我认为今天使用bash做不到你想要的;
PS4
只是没有足够的表现力来正确地为命令跟踪着色,因此没有其他钩子能在正确的时间开火

根据您的具体目标,您可能可以使用
调试
陷阱而不是
-x
。它不能完美地复制
-x
,但我们可以获得类似的效果

$ debug() {
  # print a '+' for every element in BASH_LINENO, similar to PS4's behavior
  printf '%s' "${BASH_LINENO[@]/*/+}"
  # Then print the current command, colored
  printf ' \e[36m%s\e[0m\n' "$BASH_COMMAND"
}
$ trap debug DEBUG
$ shopt -s extdebug # necessary for the DEBUG trap to carry into functions
虽然调试陷阱并不完全反映
-x
的行为,但这基本上是可行的,因此输出略有不同。下面是一个示例:

$ foo() { bar "$@"; }
$ bar() { printf '%s\n' "$@" | grep baz; }
$ foo biff bang baz
+ foo biff bang baz
++ foo biff bang baz
++ bar "$@"
+++ bar "$@"
+++ printf '%s\n' "$@"
+++ grep baz
baz
虽然我认为第二个条目是进入函数的调试陷阱,我们可以通过某种方式检测和抑制它,例如通过检查
FUNCNAME
BASH\u LINENO
,我会更新这个答案。

要点 由于终端着色的工作方式,再加上say bash跟踪的工作方式,实现起来非常难看,但下面介绍一下如何实现:

首先,您需要找到一些唯一的字符串,这些字符串既不会出现在跟踪中,也不会出现在stdout中,也不会出现在stderr中

然后,如果脚本名为
example.sh
,则运行

$ ./color_trace.sh example.sh 
其中
/color\u 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
这将为跟踪命令添加颜色

解释 其工作方式如下:

  • 在输出中插入
    $UNIQUE\u STR
  • 通过命令(
    exec&>(…)
    )路由所有输出-stdout和stderr
  • 命令(
    sed
    )假定跟踪的形式为
    ++${UNIQUE\u STR}
  • 查找带有
    $UNIQUE\u STR
    s/..\(${UNIQUE\u STR}\)…/
    )的行,并将其删除(
    /..\1+\3…/
    ,其中
    \1
    +
    \3
  • 如果该行确实包含
    $UNIQUE\u STR
    ,请告诉终端在该行之前将着色设置为红色,并在该行之后取消着色(
    /$(有色)…$(正常)/
什么不起作用 首先,函数
colored()
normal()
不接受要着色的文本。相反,它们指示终端将颜色设置为下一步要写入的内容,并在写入后指示其取消着色。这正是Unix终端中着色或任何样式的工作方式

其次,跟踪到stderr,因此它将与stderr的其他输出混合。这可以使用更改,但在这里不起作用

由于需要着色,因此假定要将跟踪与其他输出区分开来,因此假定所有输出(包括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\u STR
,我们可以使用类似以下内容:

exec 19> >(sed "s/^.*$/$(colored)&$(normal)/")
BASH_XTRACEFD=19
set -x
source "$@"

值得注意的是,即使在这个版本中,我们也必须打开和关闭每一行的颜色,因为“真实输出”的行需要不着色。

没有什么简单的设置。您可以尝试通过一个流程替换来传输所有标准错误,该流程替换将行包装成跟踪输出的一部分,但这很棘手(我还没有一个可用的答案。)嗯,别提了。由于进程替换的异步性质,无法保证跟踪输出和常规输出将正确交错。您可以将终端配置为以不同方式显示某些行,但这不可能完全在shell级别实现。正如@chepner解释的那样,这很难做到在shell级别(我没有找到任何其他方法),我做了一个骇人的解决方法:修改了
xtrace\u print
函数,以便在
fprintf
调用中直接使用颜色。执行脚本时,只需使用
bash\u colored
二进制文件。由于它是用于调试目的,可能对某些人有帮助。今天晚些时候我会将其作为答案发布。如果您使用ike
export PS4=“$(printf“%b”\033[3500万${PS4}\033[0m”)”... 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
exec 19> >(sed "s/^.*$/$(colored)&$(normal)/")
BASH_XTRACEFD=19
set -x
source "$@"