shell:将 grep 输出发送到 stderr 并保持 stdout 不变

shell: send grep output to stderr and leave stdout intact

我有一个输出到 stdout 的程序(实际上它输出到 stderr,但我可以使用 2>&1 等轻松地将其重定向到 stdout

我想 运行 grep 在程序的输出上,并将所有匹配重定向到 stderr 而将不匹配的行留在 stdout (或者,我很乐意在 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 上出现匹配行,而不是在程序退出时...

最终,我找到了 this,这给了我一个提示,可以像这样 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 也很好...

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

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

每一行都会打印; [=13=] ~ p 的结果决定了该行是打印到标准错误还是标准输出。 (您可能需要根据您的文件系统调整输出文件名。)