如何将输出重定向到标准错误流?

68

我想把 stdout 分成两份,一份输出到 stdout ,另一份输出到 stderr。听起来像是使用 tee 的工作,但是语法让我很困惑 -

./script.sh | tee stderr
当然,这里应该如何引用stderr

@JonathanLeffler 有些系统有 /dev/fd,但没有 stderr、stdout 的符号链接。我认为这包括 Solaris?当然,像 HP-UX、AIX、Irix 这样的少数异常情况没有 /dev/fd,但所有正常的系统都有。 - Nicholas Wilson
我的错,Solaris 8和9确实有/dev/stderr... - Nicholas Wilson
真的吗?Solaris 10有/dev/stderr。我没有遇到过没有/dev/std{in, out,err}的系统,但这并不意味着它们不存在。Mac OS X 10.7.5和Solaris都有这两个;Linux也有这两个。 AIX 6没有这些,HP-UX 11.00也没有这些。 - Jonathan Leffler
可能是一个有很多潜在答案的重复问题:https://dev59.com/vHRB5IYBdhLWcg3wLk1M - Eric Leschinski
bash接受/dev/stderr作为标准错误的同义词,无论文件系统中是否存在该文件;这是一个特殊情况。 - chepner
1
关于/dev/fd/2/dev/stderr; bash手册中有关重定向的说明:当文件名用于重定向时,Bash会特殊处理几个文件名,... - Joel Purra
4个回答

89

我找到的唯一支持交互和非交互shell的跨平台方法是:

command | tee >(cat 1>&2)

tee的参数是文件或文件句柄。使用过程替代,我们将输出发送到进程中。在=cat=进程中,我们将标准输出重定向到标准错误输出。Shell(bash / ksh)负责设置1和2文件描述符。


2
如果在脚本中运行,请确保添加 #!/bin/bash。因为 sh 不支持 >( xxxx ) - Sungam
这不应该成为被接受的答案吗? - a06e
这是一种绕路的方法,但在bash中总是有效的。/dev/fd/2/proc/self/fd/2 不一定存在并且具有写入权限。 - yurenchen

62
./script.sh | tee /dev/fd/2

请注意,这取决于操作系统的支持,而不是tee内置的任何功能,因此并非普遍适用(但在MacOS、Linux、Solaris、FreeBSD上可以使用,可能还有其他系统)。


这并不是真正的重定向,只是管道语法。/dev/fd/2参数对于shell来说是不透明的;它仅为tee子进程设置了stdin fd,而tee子进程则解释该参数(作为文件名,使用open()获取输出fd)。 - Nicholas Wilson
1
bash 的 manpage 暗示它接受 /dev/fd/2 作为文件描述符 2 的文件名,即使文件系统没有这样的文件。 - chepner
2
@chepner 这很有趣。但是,在这个例子中,文件名从未被bash解释,因此在这里并不起作用。 - Nicholas Wilson
4
请注意,具体取决于/dev/fd/2的内容,可能会导致翻译失败。例如,如果使用root用户登录,则您的终端将归属于root用户。因此,如果您切换到其他用户,则将无权访问/dev/fd/2 - Michał Górny
此外,/proc/self/fd/2 可能也是可用的。实际上,在我的系统 [CentOS6] 中,/dev/fd 是一个指向 /proc/self/fd 的符号链接。 - Sammitch
显示剩余3条评论

0

关于/dev/stderr权限

用户并不总是拥有写入权限:

  • /dev/stderr
  • /dev/fd/2
  • /proc/self/fd/2
    (它们都链接到同一个pts)

例如,在使用sudo之后:

sudo su - nobody -s /bin/bash

nobody@test:/$ ls -lh /dev/stderr /dev/fd/2 /proc/self/fd/2
lrwxrwxrwx 1 root   root    15 Jun 10  2021 /dev/stderr -> /proc/self/fd/2
lrwx------ 1 nobody nogroup 64 Apr  7 01:58 /dev/fd/2 -> /dev/pts/5
lrwx------ 1 nobody nogroup 64 Apr  7 01:58 /proc/self/fd/2 -> /dev/pts/5

nobody@test:/$ echo hell >&2
hell

nobody@test:/$ echo hell >/dev/fd/2
-su: /dev/fd/2: Permission denied

nobody@test:/$ echo hell >/dev/stderr 
-su: /dev/stderr: Permission denied

nobody@test:/$ echo hell > /dev/pts/5
-su: /dev/pts/5: Permission denied

另一个选择:awk

nobody@test:/$ echo hell | awk '{print>"/dev/stderr";print}' 
hell
hell

nobody@test:/$ echo ' hell   12 ' | awk '{print|"cat 1>&2";print}'
 hell   12 
 hell   12 

虽然有>"/dev/stderr",但它与shell中的不完全相同。

也许awk不如tee高效,
但值得注意的是,真正的stderr

RTSC

找出差异

待续...


-4
./script.sh 2>&1 >/dev/null | tee stderr.out

这会将 STDERR 打开到 STDOUT,然后处理掉 STDOUT。


这将所有输出导向 /dev/null - Lexi
2
它不会将所有内容都导向/dev/null,只有script.sh的标准输出被重定向了。script.sh的标准错误输出被传递给tee的标准输入,然后tee将其全部写入到tee的标准输出和文件stderr.out中...这不是问题所要求的,但恰好是我正在寻找的,所以我给它加上+1 :-) - David L.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接