如何为STDOUT和STDERR设置字体颜色

我想区分我的terminal中的STDOUT和STDERR消息。 如果脚本或命令在terminal上打印消息,我想用颜色区分; 可能吗?

(例如stderr字体颜色是红色的,而stdout字体颜色是蓝色的。)

示例(使用粗体):

$date
Wed Jul 27 12:36:50 IST 2011

$datee
bash: datee: command not found

$alias ls
alias ls='ls --color=auto -F'

$aliass ls
bash: aliass: command not found

这是我想到的一个黑客,它似乎工作:

鉴于以下可读性的别名:

 alias blue='echo -en "\033[36m"' alias red='echo -en "\033[31m"' alias formatOutput='while read line; do blue; echo $line; red; done' 

现在,您需要先将终端中的字体颜色设置为红色(默认情况下,将用于stderr)。 然后,运行你的命令并通过formatOutput定义的formatOutput管道输出stdout(它简单地将每行打印为蓝色,然后将字体颜色重置为红色):

 shell$ red shell$ ls / somenonexistingfile | formatOutput 

上面的命令将在stderr和stdout中打印,您将看到这些行的着色方式不同。

希望这可以帮助


更新:

为了使这个可重用,我把它放在一个小脚本:

 $ cat bin/run #!/bin/bash echo -en "\033[31m" ## red eval $* | while read line; do echo -en "\033[36m" ## blue echo $line echo -en "\033[31m" ## red done echo -en "\033[0m" ## reset color 

现在你可以用任何命令来使用它:

 $ run yourCommand 

在bash shell或脚本中创建一个函数:

 color()(set -o pipefail;"$@" 2>&1>&3|sed $'s,.*,\e[31m&\e[m,'>&2)3>&1 

像这样使用它:

 $ color command -program -args 

它会以红色显示命令的stderr

继续阅读,了解它是如何工作的。 这个命令演示了一些有趣的功能。

  • color()... – 创建一个名为color的bash函数。
  • set -o pipefail – 这是一个shell选项,用于保留输出到另一个命令中的命令的错误返回码。 这是在由括号创建的子shell中完成的,以便不更改外壳中的pipefail选项。
  • "$@" – 执行作为新命令的参数。 "$@"相当于"$1" "$2" ...
  • 2>&1 – 将命令的stderr重定向到stdout以便它成为sedstdin
  • >&31>&3 >&3简写,这将stdout重定向到一个新的临时文件描述符33后来被路由回stdout
  • sed ... – 由于上面的重定向, sedstdin是被执行的命令的stderr 。 它的功能是用颜色代码包围每一行。
  • $'...'一个bash构造,使它理解反斜线转义的字符
  • .* – 匹配整个行。
  • \e[31m – 导致以下字符为红色的ANSI转义序列
  • & – 扩展为整个匹配字符串的sed替换字符(本例中为整行)。
  • \e[m – 重置颜色的ANSI转义序列。
  • >&21>&2 >&2缩写,将sedstdout重定向到stderr
  • 3>&1 – 将临时文件描述符3重定向到stdout

通过将文件描述符链接到一个自定义函数来为颜色添加颜色。 添加到您的.bashrc以下:

 export COLOR_RED="$(tput setaf 1)" export COLOR_RESET="$(tput sgr0)" exec 9>&2 exec 8> >( perl -e '$|=1; while(sysread STDIN,$a,9999) {print "$ENV{COLOR_RED}$a$ENV{COLOR_RESET}"}' ) function undirect(){ exec 2>&9; } function redirect(){ exec 2>&8; } trap "redirect;" DEBUG PROMPT_COMMAND='undirect;' 

那么发生了什么? 调试陷阱在执行命令之前和之后立即执行。 因此stderr在执行命令之前被重定向,以启用红色输出。 PROMPT_COMMAND在显示提示之前进行评估,并使用此命令将stderr恢复到正常状态。 这是必要的,因为PS1PS2 (您的提示)打印在stderr上 ,我不想要红色提示。 瞧,红色输出超过stderr

你应该检查stderred: https : //github.com/sickill/stderred

是的,这本来是不可能的。 你将不得不破解tty管理(在内核中)。

我在某种程度上完成了一些小的C包装之前,我看到了其他的答案:-)可能是越野车,值是硬编码的,不要使用这个除了测试。

 #include "unistd.h" #include "stdio.h" #include <sys/select.h> int main(int argc, char **argv) { char buf[1024]; int pout[2], perr[2]; pipe(pout); pipe(perr); if (fork()!=0) { close(1); close(2); dup2(pout[1],1); dup2(perr[1],2); close(pout[1]); close(perr[1]); execvp(argv[1], argv+1); fprintf(stderr,"exec failed\n"); return 0; } close(pout[1]); close(perr[1]); while (1) { fd_set fds; FD_ZERO(&fds); FD_SET(pout[0], &fds); FD_SET(perr[0], &fds); int max = pout[0] > perr[0] ? pout[0] : perr[0]; int v = select(max+1, &fds, NULL, NULL, NULL); if (FD_ISSET(pout[0], &fds)) { int r; r = read(pout[0], buf, 1024); if (!r) {close(pout[0]); continue;} write(1, "\033[33m", 5); write(1, buf, r); write(1, "\033[0m", 4); } if (FD_ISSET(perr[0], &fds)) { int r; r = read(perr[0], buf, 1024); if (!r) {close(perr[0]); continue;} write(2, "\033[31m", 5); write(2, buf, r); write(2, "\033[0m", 4); } if (v <= 0) break; } return 0; } 

编辑:与shell解决方案相比,这个将更经常地保留行/字符的顺序。 (不可能像直接读取tty一样准确。)命中^ C不会显示一个丑陋的错误信息,并且在这个例子中表现正确:

 ./c_color_script sh -c "while true; do (echo -na; echo -nb 1>&2) done" 

我很惊讶,没有人真正知道如何为stdio流着色。 这将为整个(子)shell着色stderr红色:

 exec 3>&2 exec 2> >(sed -u 's/^\(.*\)$/'$'\e''[31m\1'$'\e''[m/' >&3) 

在这种情况下, &3将保存原始的stderr流。

你不应该传递任何命令来exec ,只有重定向。 这种特殊情况会导致exec将当前(子)shell的stdio流替换为接收到的流。

有几个警告:

  • 由于sed将在并行子shell中持久运行,因此在写入彩色stdio后立即输出的任何直接输出都可能会击败sedtty
  • 这个方法使用一个FIFO文件描述符; FIFO节点只处理线路。 如果您不向流中写入换行符,则会缓冲输出,直到遇到换行符为止。 这不是sed的部分缓冲:这是如何这些文件类型的功能。

最棘手的问题是第一个,但即使使用默认颜色,也可以通过对所有输出应用类似的处理或多或少地避免竞态条件。

您可以通过使用普通管道运算符( | )管道到相同的sed命令,对单个命令执行类似的处理。 管道链是同步执行的,所以没有竞争条件会发生,尽管管道链中的最后一个命令默认收到它自己的子shell。