IO重定向 - 交换Stdout和stderr



给定一个shell脚本:

#!/bin/sh
echo "I'm stdout";
echo "I'm stderr" >&2;

是否有一种调用该脚本的方法,以便只有stderr才能打印出来,当命令的最后一部分为2>/dev/null时,即

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

或,或者,

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

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

% (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)上收到它。

出于完整的目的,基于上面的 @200_success的评论,最好使用1>&3-移动文件描述符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:

$ (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

移动文件描述符(1>&3-)是不可移植的,并非所有POSIX Shell实现都支持它。这是KSH93-主义和狂欢主义。(更多信息在此处https://unix.stackexchange.com/questions/65000/practical-use-for-moving-file-descriptors)

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

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

打印当前工作目录的内容,

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

Bash Hackers Wiki在这种情况下可能非常有用。在这些答案中没有提到的一种方法,所以我要把我的两分钱。

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

因此,您可以按以下方式进行:

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

这意味着:

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

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

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

有趣的是,我不能简单地做>&-,而不是>/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

最新更新