IO重定向 - 交换stdout和stderr

Ric*_*ard 32 shell sh io-redirection

给出一个shell脚本:

#!/bin/sh

echo "I'm stdout";
echo "I'm stderr" >&2;
Run Code Online (Sandbox Code Playgroud)

有没有办法调用该脚本,只有stderr打印出来,当命令的最后一部分是2>/dev/null时,即

$ > sh myscript.sh SOME_OPTIONS_HERE 2>/dev/null
I'm stderr
Run Code Online (Sandbox Code Playgroud)

或者,或者:

$ > sh myscript.sh SOME_OPTIONS_HERE >/dev/null
I'm stdout
Run Code Online (Sandbox Code Playgroud)

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

unb*_*eli 35

% (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
Run Code Online (Sandbox Code Playgroud)

说明3>&2 2>&1 1>&3:

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

  • [移动文件描述符](http://www.gnu.org/software/bash/manual/bash.html#Moving-File-Descriptors)使用`1>&3-`比稍微复制文件描述符要好一些,因为你最终不会得到一个开放的FD 3. (12认同)
  • 虽然我已经说过这种或那种方式,但我想强调这一点是为了完整性:我们可能希望在交换后关闭临时文件描述符,因为我们不再需要它,所以完整的例子是:`myscript .sh 3>&2 2>&1 1>&3 3>& - ...`.在bash的情况下,可以动态分配文件描述符:`myscript.sh {FD}>&2 2>&1 1>&$ FD {FD}>& - )...`,其中FD是变量名. (4认同)
  • @Richard只是为了避免混淆双重重定向的shell.无法在同一命令中重定向FD两次. (2认同)

Syl*_*oux 13

为了完整起见,基于上面@ 200_success的注释,最好使用以下方法移动文件描述符3 1>&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
Run Code Online (Sandbox Code Playgroud)

而不是基于每个进程交换文件描述符,使用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
Run Code Online (Sandbox Code Playgroud)


Dac*_*cav 6

庆典黑客维基可以在这样的事情是非常有用的.有一种做法,这些答案中没有提及,所以我会把我的两分钱.

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

所以你可以这样做:

./myscript.sh 2>&1 >/dev/null
Run Code Online (Sandbox Code Playgroud)

这意味着:

  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
Run Code Online (Sandbox Code Playgroud)

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

./myscript.sh >/dev/null 2>&1
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为:

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


Gre*_*bet 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>&-
Run Code Online (Sandbox Code Playgroud)

将当前工作目录的内容打印出stderr.

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