【问题标题】:In the Unix/Linux shell programming:the difference between > and >&Unix/Linux shell编程中:>和>&的区别
【发布时间】:2011-10-01 12:30:52
【问题描述】:
int main(void)
{

  char buf[] = "standard err, output.\n";

  printf("standard output.\n");

  if (write(STDERR_FILENO,buf, 22) != 22)
      printf("write err!\n");

  exit(0);
}

编译使用:

gcc -Wall text.c

然后在shell中运行:

  1. ./a.out > outfile 2 >& 1

    结果:outfile的内容是:

    standard err, output.                
    standard output.
    
  2. ./a.out 2 >& 1 >outfile

    结果:

    这首先打印到终端:standard err, output.
    而outfile的内容是:standard output.

问题:

  • 我想问一下2 >& fd2 > file的区别。
    它们都等于函数 dup() 吗?

  • 另一个问题:为什么outfile的内容是:

     standard err, output. 
     standard output.
    

    我预计 outfile 的内容是:

     standard output. 
     standard err, output 
    

【问题讨论】:

  • “外壳”含糊不清;每个 shell 使用不同的语法。
  • 顺便说一句,为什么要拨打write()?要打印到标准错误,写fprintf(stderr, "whatever"); 要容易得多
  • @Wooble:不合格,“外壳”是指 POSIX 指定的外壳。
  • fd 号和 > 符号之间不应有任何空格。如果文件描述符编号后面有空格,则不会被识别,并将传递给命令(本例中为 a.out)

标签: c linux shell unix


【解决方案1】:

实际上,在 bash 中,>&dup2 非常相似。也就是说,应用它的文件描述符将引用与右侧描述符相同的文件。所以:

$ ./a.out > outfile 2>& 1

它将stdout(1) 重定向到文件outfile,然后,dup2 stderr(2) 将引用与stdout(1) 相同的文件。也就是说,stdout 和 stderr 都被重定向到文件。

$ ./a.out 2>& 1 >outfile

它将重定向 stderr(2) 以引用与 stdout(1) 相同的文件,即控制台,然后将重定向 stdout(1) 以引用文件outfile。即stderr会输出到控制台,stdout会输出到文件。

这正是你得到的。

【讨论】:

  • 另一个问题:为什么outfile的内容是:标准错误,输出。 \n 标准输出。我认为 outfile 的内容应该是:标准输出。 \n 标准错误,输出
  • 那是因为printf 的输出是缓冲的(当tty 时行缓冲,重定向到常规文件时完全缓冲),但write 的输出没有缓冲。所以会立即看到write,然后看到printf。如果您将其更改为 fprintf(stderr,...,正如我在其他地方评论的那样,您将获得预期的行为。
【解决方案2】:

范式混合


虽然有理由刻意做所有这些事情,但作为一种学习体验,在我所谓的“域边界”上混合操作可能会令人困惑。

缓冲与非缓冲 I/O

printf() 被缓冲,write() 是直接系统调用。无论如何,写入都会立即发生,当输出是终端时, printf 将(通常)逐行缓冲,当输出是真实文件时,将逐块缓冲。在文件输出情况(重定向)中,您的实际 printf 输出只会在您从 main() 返回或以其他方式调用 exit(3) 时发生,除非您 printf 一大堆东西。

历史 csh 重定向与 bash 重定向

Bill Joy 在 UCB 编写的现已被遗忘(但通常仍处于默认安装)的 csh,当时一名研究生有一些不错的功能已导入到厨房水槽外壳中,或者将所有外壳功能组合在一起的。是的,我在这里谈论 bash。因此,在 csh 中,重定向标准输出和标准错误的方法就是简单地说 cmd >& file,这确实比“官方”Bourne shell 的工具包方法更加文明假如。但是 Bourne 语法在其他地方也有它的优点,并且无论如何作为主导范式幸存下来。

但是 bash “本机”重定向功能有些复杂,我不会尝试在 SO 答案中总结它们,尽管其他人似乎已经有了一个良好的开端。在任何情况下,您都在一个测试中使用真正的 bash 重定向,而 bash 在另一个测试中也支持的 legacy-csh 语法,以及一个本身混合了范式的程序。从 shell 的角度来看,主要问题是重定向顺序在 bash 样式语法中非常重要,而 csh 样式语法只是指定最终结果。

【讨论】:

  • 感谢您的回答:缓冲与非缓冲 I/O
【解决方案3】:

这里有几个松散相关的问题。

风格评论:我推荐使用2>&1,不带空格。我什至不知道间隔版本可以工作(我怀疑它在 80 年代中期的 Bourne shell 中没有),而压缩版本是正统的编写方式。

文件描述符 I/O 重定向符号在 C shell 及其衍生版本中并非全部可用;它们可在 Bourne shell 及其衍生工具(Korn shell、POSIX shell、Bash...)中使用。

>file2>file2>&1 之间的区别是 shell 要做的。前两个安排写入文件描述符的输出(第一种情况下为 1,即标准输出;第二种情况下为 2,即标准错误)以转到指定文件。这意味着程序写入标准输出的任何内容都会转到文件中。第三种表示法安排 2(标准错误)与 1(标准输出)转到相同的文件描述符;写入标准错误的任何内容都会与标准输出进入同一个文件。它是使用dup2() 轻松实现的。但是,程序中的标准错误流会有自己的缓冲区,而程序中的标准输出流也会有自己的缓冲区,所以输出的交错并不能完全确定是否输出到文件。

您以两种不同的方式运行命令,并且(毫不奇怪)得到两个不同的结果。

  1. ./a.out > outfile 2>&1

    I/O 重定向从左到右处理。第一个发送标准输出到outfile。第二个将标准错误发送到与标准输出相同的位置,因此它也发送到outfile

  2. ./a.out 2>&1 >outfile

    第一次重定向将标准错误发送到标准输出的地方,即当前终端。然后第二个重定向将标准输出发送到文件(但将标准错误留在终端)。

程序使用printf() 函数和write() 系统调用。当使用printf() 函数时,它会缓冲其输出。如果输出到终端,那么它通常是“行缓冲的”,所以当一个换行符被添加到缓冲区时,输出就会出现。但是,当输出到文件时,它是“完全缓冲的”,并且在文件流被刷新或关闭或缓冲区填满之前不会出现输出。请注意,stderr 没有完全缓冲,因此写入它的输出会立即出现。

如果您在没有任何 I/O 重定向的情况下运行程序,您将看到:

standard output. 
standard err, output

相比之下,write() 系统调用会立即将数据传输到输出文件描述符。在示例中,您写入标准错误,您写入的内容将立即出现。如果您使用fprintf(stderr, ...),也会发生同样的情况。但是,假设您修改了程序以写入 STDOUT_FILENO;那么当输出到文件时,输出将按顺序出现:

standard err, output
standard output. 

因为write() 是无缓冲的,而printf() 是缓冲的。

【讨论】:

  • 其实右边的空格是可以的,但是第一个不行。所以你可以写a.out 2>& 1。如果您使用a.out 2 >&1,那么2 是程序参数,而不是shell fd。
  • 谢谢,罗德里戈。这就说得通了。而且我认为“无空格”的建议是最好的。
  • 感谢您的回答:缓冲和非缓冲。
【解决方案4】:

2>&1 部分使 shell 执行类似的操作:

dup2(1, 2);

这使得 fd 2 成为 fd 1 的“副本”。

2> file 被解释为

fd = open(file, ...);
dup2(fd, 2);

打开一个文件并将文件描述符放入插槽 2。

【讨论】:

  • 另一个问题:为什么outfile的内容是:标准错误,输出。 \n 标准输出。我认为 outfile 的内容是:标准输出。 \n 标准错误,输出
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-10
  • 2015-04-07
  • 1970-01-01
  • 1970-01-01
  • 2017-06-01
相关资源
最近更新 更多