首页 > 解决方案 > shell:将 grep 输出发送到 stderr 并保持 stdout 不变

问题描述

我有一个输出到的程序stdout(实际上它输出到stderr,但我可以轻松地将其重定向到stdoutwith2>&1或类似的。

我想grep在程序的输出上运行,并将所有匹配项重定向到,stderr同时保留不匹配的行stdout(或者,我很乐意让所有行 - 而不仅仅是不匹配的行 - on stdout

例如

$ myprogram() {
cat <<EOF
one line
a line with an error
another line
EOF
}
$ myprogram | greptostderr error >/dev/null
a line with an error
$ myprogram | greptostderr error 2>/dev/null
one line
another line
$

一个简单的解决方案是:

myprogram | tee logfile
grep error logfile 1>&2
rm logfile

但是,我宁愿stderr在它们发生时得到匹配的行,而不是在程序退出时......

最终,我找到了这个,这给了我一个 POSIX 解决方案的提示,如下所示:

greptostderr() {
  while read LINE; do
    echo $LINE
    echo $LINE | grep -- "$@" 1>&2
  done
}

无论出于何种原因,这不会输出任何内容(可能是缓冲问题)。

一个看起来有点难看的解决方案是这样的:

greptostderr() {
  while read LINE; do
    echo $LINE
    echo $LINE | grep -- "$@" | tee /dev/stderr >/dev/null
  done
}

有没有更好的方法来实现这一点?

理想情况下,我正在寻找一个 POSIX shell 解决方案,但bash也很好......

标签: bashredirectshstdoutstderr

解决方案


我会使用awk而不是grep,它可以让您更灵活地处理匹配和不匹配的行。

myprogram | awk -v p=error '{ print > ($0 ~ p ? "/dev/stderr" : "/dev/stdout")}'

每一行都会被打印出来;的结果$0 ~ p确定该行是打印到标准错误还是标准输出。(您可能需要根据您的文件系统调整输出文件名。)


推荐阅读