bash:以红色打印stderr


122

有没有一种方法可以使bash 以红色显示stderr消息?


4
我猜bash永远不会使输出变色:某些程序可能想解析某些内容,而变色会破坏带有转义序列的数据。我想一个GUI应用程序应该处理颜色。
kolypto

将BalázsPozsár和killdash9的答案结合起来可以得出清晰的结论: function color { "$@" 2> >(sed $'s,.*,\e[31m&\e[m,') } 适用于bash和zsh。不能将其添加为答案B / C信誉。
海因里希·哈特曼

1
我正在等待修改bash的答案。下面的所有解决方案实际上都修改了stderr,甚至可能在stdout上对其重新排序,这在必须保留stderr的确切字节序列(例如管道)时会破坏事情。
masterxilo

Answers:


98
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)

6
大!但我想知道是否有办法使它永久化:)
kolypto

9
大提示!建议:通过>&2在之前添加; done),实际上将用于stderr的输出写入stderr。如果您想捕获程序的正常输出,这将很有帮助。
henko 2012年

8
以下使用tput,在我看来更易读:command 2> >(while read line; do echo -e "$(tput setaf 1)$line$(tput sgr0)" >&2; done)
Stefan Lasiewski 2013年

2
我认为为每条输出线执行2个tput进程根本不是一件好事。也许您可以将tput命令的输出存储在变量中,并将其用于每个回显。但是话又说回来,可读性并不是真的更好。
巴拉兹Pozsár

1
此解决方案不保留空格,但为简洁起见,我喜欢它。 IFS= read -r line应该有所帮助,但没有帮助。不知道为什么。
Max Murphy

89

方法1:使用流程替代:

command 2> >(sed $'s,.*,\e[31m&\e[m,'>&2)

方法2:在bash脚本中创建一个函数:

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

像这样使用它:

$ color command

两种方法都会stderr以红色显示命令。

继续阅读以了解方法2的工作原理。此命令演示了一些有趣的功能。

  • color()... —创建一个名为color的bash函数。
  • set -o pipefail—这是一个shell选项,用于保留命令的错误返回代码,该命令的输出通过管道传递到另一个命令中。这是在由括号创建的子外壳中完成的,以免更改外壳中的pipefail选项。
  • "$@"—将函数的参数作为新命令执行。 "$@"相当于"$1" "$2" ...
  • 2>&1-重定向的stderr命令,以stdout使其变成sedstdin
  • >&3—的简写形式1>&3,它重定向stdout到新的临时文件描述符33被路由回stdout以后。
  • sed ...—由于上面的重定向,sed's stdinstderr已执行命令的。其功能是用颜色代码包围每行。
  • $'...' 一个bash构造,使其理解反斜杠转义的字符
  • .* —匹配整行。
  • \e[31m —导致以下字符变为红色的ANSI转义序列
  • &sed替换字符,扩展为整个匹配的字符串(在这种情况下为整行)。
  • \e[m —重设颜色的ANSI转义序列。
  • >&2-速记1>&2,这个重定向sedstdoutstderr
  • 3>&1—将临时文件描述符重定向3stdout

9
+1个最佳答案!绝对低估了!
muhqu 2013年

3
很好的答案甚至更好的解释
Daniel Serodio

为什么需要进行所有其他重定向?似乎过大了
qodeninja

1
有办法使它起作用zsh吗?
Eyal Levin

1
ZSH无法识别速记重定向形式。它只需要zsh: color()(set -o pipefail;"$@" 2>&1 1>&3|sed $'s,.*,\e[31m&\e[m,'1>&2)3>&1
再加上

26

您还可以签出stderred:https : //github.com/sickill/stderred


哇,这个实用程序很棒,它唯一需要做的就是拥有一个apt存储库,只需一行即可为所有用户安装它,而无需做更多的工作来启用它。
sorin 2012年

当我在单独的终端中使用构建脚本对其进行测试时,似乎工作得很好,但我犹豫要在全球范围内使用它.bashrc。不过谢谢!
乔尔·普拉

2
在OS X El Capitan中,此工作方式(DYLD_INSERT_LIBRARIES)在系统二进制文件中被“破坏”,因为它们受到SIP的保护。因此,最好使用其他答案中给出的bash选项。
hmijail '16

1
对于MacOS的@hmijail,请关注github.com/sickill/stderred/issues/60,以便我们可以找到一种解决方法,虽然部分解决方法已经存在,但存在一些问题。
索林

15

使stderr永久变为红色的重击方法是使用“ exec”重定向流。将以下内容添加到您的bashrc中:

exec 9>&2
exec 8> >(
    while IFS='' read -r line || [ -n "$line" ]; do
       echo -e "\033[31m${line}\033[0m"
    done
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; }
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

我以前在此发布过:如何为STDOUT和STDERR设置字体颜色



1
到目前为止,这是最好的答案。易于实现,无需安装/不需要sudo特权,并且可以推广到所有命令。
卢克·戴维斯

1
不幸的是,这在命令链接(命令&& nextCommand || errorHandlerCommand)中不能很好地发挥作用。错误输出在errorHandlerCommand输出之后。
carlin.scott

1
同样,如果我source ~/.bashrc两次这样做,我的终端基本上会锁定。
Dolph

@Dolf:在我的bashrc中,我很容易用周围的if语句来防止这种情况发生,以防止重新加载此代码。否则,问题是重定向已经执行之后,重定向了'exec 9>&2'。如果您知道> 2最初指向的位置,则可以将其更改为常数。
gospes '18 -10-1


7

我编写了一个包装脚本,以纯bash形式实现了BalázsPozsár的答案。将其保存在$ PATH和前缀命令中以使其输出着色。

    #!/ bin / bash

    如果[$ 1 ==“ --help”]; 然后
        echo“执行命令并为发生的所有错误着色”
        echo“示例:`basename $ {0}`wget ...”
        回声“(c)o_O Tync,ICQ#1227-700,尽情享受!”
        出口0
        科幻

    #临时文件以捕获所有错误
    TMP_ERRS = $(mktemp)

    #执行命令
    “ $ @” 2>>(在读取行时;执行echo -e“ \ e [01; 31m $ line \ e [0m” | tee --append $ TMP_ERRS;完成)
    EXIT_CODE = $?

    #再次显示所有错误
    如果[-s“ $ TMP_ERRS”]; 然后
        echo -e“ \ n \ n \ n \ e [01; 31m ===错误=== \ e [0m”
        猫$ TMP_ERRS
        科幻
    rm -f $ TMP_ERRS

    #完成
    退出$ EXIT_CODE


2
如果将“ | tee ...”放在“完成”之后,则可以提高效率。
朱利诺

3

您可以使用这样的功能


 #!/bin/sh

color() {
      printf '\033[%sm%s\033[m\n' "$@"
      # usage color "31;5" "string"
      # 0 default
      # 5 blink, 1 strong, 4 underlined
      # fg: 31 red,  32 green, 33 yellow, 34 blue, 35 purple, 36 cyan, 37 white
      # bg: 40 black, 41 red, 44 blue, 45 purple
      }
string="Hello world!"
color '31;1' "$string" >&2

我附加>&2以打印到stderr


4
无法解决问题。您没有提供分离标准输出,这是什么OP感兴趣标准错误的方式。
杰里米·维瑟

1

我有一个O_o Tync脚本的稍微修改的版本。我需要为OS X Lion制作这些mod,但这并不完美,因为脚本有时会在包装命令执行之前完成。我增加了睡眠,但是我敢肯定有更好的方法。

#!/bin/bash

   if [ $1 == "--help" ] ; then
       echo "Executes a command and colorizes all errors occured"
       echo "Example: `basename ${0}` wget ..."
       echo "(c) o_O Tync, ICQ# 1227-700, Enjoy!"
       exit 0
       fi

   # Temp file to catch all errors
   TMP_ERRS=`mktemp /tmp/temperr.XXXXXX` || exit 1

   # Execute command
   "$@" 2> >(while read line; do echo -e "$(tput setaf 1)$line\n" | tee -a $TMP_ERRS; done)
   EXIT_CODE=$?

   sleep 1
   # Display all errors again
   if [ -s "$TMP_ERRS" ] ; then
       echo -e "\n\n\n$(tput setaf 1) === ERRORS === "
       cat $TMP_ERRS
   else
       echo "No errors collected in $TMP_ERRS"
   fi
   rm -f $TMP_ERRS

   # Finish
   exit $EXIT_CODE

1

该解决方案为我工作:https : //superuser.com/questions/28869/immediately-tell-which-output-was-sent-to-stderr

我已经将此功能放在我的.bashrc或中.zshrc

# Red STDERR
# rse <command string>
function rse()
{
    # We need to wrap each phrase of the command in quotes to preserve arguments that contain whitespace
    # Execute the command, swap STDOUT and STDERR, colour STDOUT, swap back
    ((eval $(for phrase in "$@"; do echo -n "'$phrase' "; done)) 3>&1 1>&2 2>&3 | sed -e "s/^\(.*\)$/$(echo -en \\033)[31;1m\1$(echo -en \\033)[0m/") 3>&1 1>&2 2>&3
}

然后例如:

$ rse cat non_existing_file.txt

将给我红色输出。


您可以在set -o pipefail;之前添加(eval重定向退出代码
kvaps

还添加了"eval以保留参数中的空格
kvaps


0

使用fifos的版本

mkfifo errs
stdbuf -o0 -e0 -i0 grep . foo | while read line; do echo -e "\e[01;31m$line  \e[0m" >&2; done &
stdbuf -o0 -e0 -i0 sh $script 2>errs
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.