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

时间:2022-03-23 00:24:51

I'm perplexed. Under OSX and Linux (using BASH, TCSH, FISH, and DASH) this code successfully reads user input when that input is provided directly through the terminal but not when the user input is provided through a pipe.

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

Even more perplexing though: I don't expect this code to work at all! This code is READING from STDOUT. I'd expect the read call to return an error since I'm essentially reading from a write only pipe.

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

#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;
}

To build (assuming you named the sample test.c):

要构建(假设您将示例命名为test.c):

$ gcc -o test test.c

Then, this reads the user input:

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

$ ./test
abcd
I read 'abcd

'

But, this does not:

但是,这不是:

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

Any insights?

2 个解决方案

#1


4  

The terminal implements stdin and stdout as the same read/write pseudo terminal and the two descriptors both map to the same thing by default. However, when you use a pipe the shell creates an actual pipe with distinct read and write ends, the read-end of which replaces the stdin of your program but not the stdout. Thus you cannot read the pipe's input from stdout, which is no longer the same thing as stdin.

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

Here's a little test showing what happens to the descriptors:

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

#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;
}

And when run:

运行时:

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

Under Linux you can also do:

在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

#2


3  

Most likely this is working, because on a terminal the stdin and stdout may point to the same object. That this is not correct behaviour you can see when you use pipes, because the output buffer should't be really provide data when read from. So this is maybe some bug or a sideeffect in the implementation of the terminal.

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

#1


4  

The terminal implements stdin and stdout as the same read/write pseudo terminal and the two descriptors both map to the same thing by default. However, when you use a pipe the shell creates an actual pipe with distinct read and write ends, the read-end of which replaces the stdin of your program but not the stdout. Thus you cannot read the pipe's input from stdout, which is no longer the same thing as stdin.

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

Here's a little test showing what happens to the descriptors:

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

#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;
}

And when run:

运行时:

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

Under Linux you can also do:

在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

#2


3  

Most likely this is working, because on a terminal the stdin and stdout may point to the same object. That this is not correct behaviour you can see when you use pipes, because the output buffer should't be really provide data when read from. So this is maybe some bug or a sideeffect in the implementation of the terminal.

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