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.
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.
#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):
$ 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?
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
.
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:
$ 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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With