如何将stdout和stderr重定向到此处

时间:2013-09-12 00:45:41

标签: bash time io-redirection

我有这个:

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

中的125 + 0记录

125 + 0记录

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

真实的0m0.133s

用户0m0.000s

sys 0m0.020s

$ cat file

文件为空。

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

2 个答案:

答案 0 :(得分:4)

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

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

......产生:

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

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

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

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

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

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

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

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

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

    sh -c ...
    

    重定向是:

    1. 将stderr发送到stdout目前正在进行的任何地方
    2. 再次将stderr发送到stdout(这没有新效果)
    3. 将标准输出发送至file
    4. 所以sh -c运行时它的stderr会转到你的stdout,而它的stdout会转到文件file。正如您所注意到的,dd将其输出打印到(其)stderr,这是sh -c ...的stderr,这是您的标准输出。

      如果你跑:

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

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

      如果您尝试:

      2>file time ...
      

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

      (time ...) 2>file
      

      或子块pje illustrated

      { time ...; } 2>file
      

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