关于bash:如何将stdout和stderr重定向到此处

How is the stdout and stderr redirected here

我有这个:

1
$ time 2>&1 sh 2>&1 -c"dd if=/dev/zero of=ddfile bs=512 count=125 && sync"> file

125 + 0记录

125 + 0记录了

复制64000字节(64 kB),0.000617678 s,104 MB / s

真正的0m0.133s

用户0m0.000s

sys 0m0.020s

1
$ cat file

文件是空的。

问题:我理解ddtime都将他们的o / p发送到stderr。 但是因为我将stderr重定向到stdout for timesh(这是dd的父进程)并且还发送了stdout来转到文件我希望将事情写入文件。 为什么没有发生?


如果要重定向time及其子命令的输出,可以将它们分组为代码块并将其stderr重定向到该文件:

1
$ { time sh -c"dd if=/dev/zero of=ddfile bs=512 count=125 && sync"; } 2> file

......产生:

1
2
3
4
5
6
7
8
$ cat file
125+0 records in
125+0 records out
64000 bytes (64 kB) copied, 0.000617678 s, 104 MB/s

real        0m0.133s
user        0m0.000s
sys         0m0.020s

首先,除了一些例外,重定向通常发生在它们被写入的位置。它们从命令和参数序列中删除;只有剩下的,非重定向,单词参与。从而:

1
3>&1 foo 1>&2 arg1 2>&3 arg2 3>&- arg3

运行foo arg1 arg2 arg3与stdout和stderr交换,因为:

  • 3>&1在fd 3上复制(更确切地说,dup2)fd 1(stdout)
  • 1>&2在fd 1上制作fd 2(stderr)的副本(所以现在stdout和stderr都会去任何地方)
  • 2>&3在fd 2(stderr)上制作fd 3(保存的原始标准输出)的副本
  • 3>&-关闭fd 3。
  • (值得注意的例外是管道输出"先发生",即使管道符号位于简单命令部分的末尾。)

    其次,正如pje所说,time是一个内置的bash。 time foo运行命令,然后将时间摘要打印到bash的stderr。首先有效地删除time关键字,然后像往常一样处理剩余的命令序列。 (即使命令是管道,这也适用:time乘以整个管道。)

    在这种情况下,命令序列是一个简单的命令,带有重定向:

    1
    2>&1 sh 2>&1 -c ... > file

    每次重定向都发生在它写入的位置,剩下的序列是:

    1
    sh -c ...

    重定向是:

  • 将stderr发送到stdout当前的去向
  • 再次将stderr发送到stdout(这没有新效果)
  • 将stdout发送到file
  • 所以sh -c运行时它的stderr会进入你的stdout,而它的stdout会转到文件file。如您所知,dd将其输出打印到(其)stderr,即sh -c ...的stderr,这是您的标准输出。

    如果您运行:

    1
    time 2>file dd if=/dev/zero of=ddfile bs=512 count=125

    您将在stderr(例如,屏幕)上获得time的stderr输出,并在文件file上获得dd的stderr。 (无论你滑动2>file部分多远,只要它仍然是简单命令的一部分,就会发生这种情况。)

    如果你试试:

    1
    2>file time ...

    你会发现time的输出被重定向,但这完全击败了内置的time,而不是运行/usr/bin/time。为了让bash的内置功能成为可能,time必须在前面。你可以做一个子shell:

    1
    (time ...) 2>file

    或者如图所示的子块:

    1
    { time ...; } 2>file

    (语法是笨拙的,子块作为空格和/或分号是必需的,但它避免了fork系统调用)。