为什么我可以从STDOUT读取并获得用户的终端输入?

时间:2014-02-22 16:24:22

标签: c linux

我很困惑。在OSX和Linux下(使用BASH,TCSH,FISH和DASH),当直接通过终端提供输入时,此代码成功读取用户输入,而不是通过管道提供用户输入时。

更令人困惑的是:我不希望这段代码完全奏效!此代码是从STDOUT读取的。我希望读取调用返回错误,因为我实际上是从只写管道读取。

#include <unistd.h>
#include <stdio.h>

int main(int argc, char** argv) {
  char buffer[11];
  size_t nread;
  if((nread=read(1, buffer, sizeof(buffer)-1)) <= 0) {
    fprintf(stderr, "error!\n");
    return 1;
  }
  buffer[nread] = '\0';
  printf("I read '%s'\n", buffer);
  return 0;
}

构建(假设您将样本命名为test.c):

$ gcc -o test test.c

然后,这将读取用户输入:

$ ./test
abcd
I read 'abcd

'

但是,这不是:

$ echo "abcd" | ./test
<< program still waiting for the read to finish

任何见解?

2 个答案:

答案 0 :(得分:4)

终端实现stdinstdout作为相同的读/写伪终端,两个描述符默认映射到同一个东西。但是,当您使用管道时,shell会创建一个具有不同读写结尾的实际管道,其读取端将替换程序的stdin,而不是stdout。因此,您无法从stdout读取管道输入,这与stdin不再相同。

这是一个小测试,显示描述符会发生什么:

#include <stdio.h>
#include <sys/stat.h>

int main (void) {
    struct stat st;
    (void) fstat(0, &st);
    (void) printf("stdin  dev=%ld node=%ld\n", (long) st.st_dev, (long) st.st_ino);
    (void) fstat(1, &st);
    (void) printf("stdout dev=%ld node=%ld\n", (long) st.st_dev, (long) st.st_ino);
    return 0;
}

跑步时:

$ ./fdcheck
stdin  dev=389931976 node=4338
stdout dev=389931976 node=4338
$ echo foo | ./fdcheck
stdin  dev=0 node=-5077412903630272353
stdout dev=389931976 node=4338

在Linux下你也可以这样做:

$ readlink /proc/self/fd/0
/dev/pts/4
$ echo foo | readlink /proc/self/fd/0
pipe:[353335]
$ echo foo | readlink /proc/self/fd/1
/dev/pts/4

答案 1 :(得分:3)

这很可能是有效的,因为在终端上stdinstdout可能指向同一个对象。当你使用管道时,这是不正确的行为,因为输出缓冲区在读取时不应该真正提供数据。所以这可能是终端实施中的一些错误或副作用。