IO重定向 - 交换stdout和stderr

时间:2012-11-08 22:52:50

标签: shell sh io-redirection

给出一个shell脚本:

#!/bin/sh

echo "I'm stdout";
echo "I'm stderr" >&2;

当命令的最后一部分是2> / dev / null,即

时,有没有办法调用该脚本以便只打印出stderr?
$ > sh myscript.sh SOME_OPTIONS_HERE 2>/dev/null
I'm stderr

或者,或者:

$ > sh myscript.sh SOME_OPTIONS_HERE >/dev/null
I'm stdout

在一组演讲幻灯片结束时这是一个问题,但经过将近一天的工作,我几乎可以肯定它是某种错字。透视不起作用。 2>& - 不起作用。我没有想法!

4 个答案:

答案 0 :(得分:32)

% (sh myscript.sh 3>&2 2>&1 1>&3) 2>/dev/null
I'm stderr
% (sh myscript.sh 3>&2 2>&1 1>&3) >/dev/null 
I'm stdout

3>&2 2>&1 1>&3的解释:

  • 3>&2表示文件描述符2(fd 2)(stderr)的复制,名为fd 3(文件描述符3)。 复制文件描述符,它不会像tee那样复制流。
  • 2>&1表示sh myscript.sh的fd 2成为fd 1(stdout)的副本。现在,当myscript写入它的stderr(它是fd 2)时,我们会在stdout(我们的fd 1)上收到它。
  • 1>&3表示sh myscript.sh的fd 1成为fd 3(stderr)的副本。现在,当myscript写入stdout(它是fd 1)时,我们会在stderr(我们的fd 2)上收到它。

答案 1 :(得分:12)

为了完整起见,基于上面@ 200_success的评论,使用1>&3- move文件描述符3可能更好:

$ (sh myscript.sh 3>&2 2>&1 1>&3-) 2>/dev/null
I'm stderr
$ (sh myscript.sh 3>&2 2>&1 1>&3-) >/dev/null 
I'm stdout

不是在每个进程的基础上交换文件描述符,而是使用exec,你可以交换stdin& stderr用于当前shell启动的所有后续命令:

$ (exec 3>&2 2>&1 1>&3- ; sh myscript.sh ; sh myscript.sh ) 2>/dev/null
I'm stderr
I'm stderr
$ (exec 3>&2 2>&1 1>&3- ; sh myscript.sh ; sh myscript.sh ) >/dev/null 
I'm stdout
I'm stdout

答案 2 :(得分:5)

移动文件描述符(1>&3-)不可移植,并非所有POSIX shell实现都支持它。这是一个ksh93-ism和bash-ism。 (更多信息请https://unix.stackexchange.com/questions/65000/practical-use-for-moving-file-descriptors

执行重定向后,也可以关闭FD 3。

ls 3>&2 2>&1 1>&3 3>&-

将当前工作目录的内容输出到stderr。

语法3>&-3<&-关闭文件描述符3。

答案 3 :(得分:5)

bash hackers wiki在这类事情中非常有用。 有一种做法,这些答案中没有提及,所以 我会把我的两分钱。

>&N的语义,对于数字N,表示重定向到目标 文件描述符N 。单词 target 很重要,因为描述符可以稍后更改目标,但是一旦我们复制了该目标,我们就不在乎了。这就是为什么我们声明重定向的顺序是相关的。

所以你可以这样做:

./myscript.sh 2>&1 >/dev/null

这意味着:

  1. 将stderr重定向到stdout的目标,即stdout输出流。现在 stderr复制了stdout的目标

  2. 将stdout更改为/ dev / null。这不会影响stderr,因为它“复制”了 我们改变它之前的目标。

  3. 不需要第三个文件描述符。

    有趣的是,我不能简单地>&-而不是>/dev/null。这实际上关闭了stdout,所以我得到一个错误(在stderr的目标上,这是实际的stdout,当然:D)

    line 3: echo: write error: Bad file descriptor
    

    您可以通过尝试交换重定向来查看订单是否相关:

    ./myscript.sh >/dev/null 2>&1
    

    这不起作用,因为:

    1. 我们将stdout的目标设置为/dev/null
    2. 我们将stderr的目标设置为stdout的目标,即/dev/null