0

I pass 1(stdout)/2(stderr) to read system call but it still works fine. Then I pass 0(stdin) to write system call and find out it works too!

int main(int argc, char** argv){
    char buf[1024] = "abcdefghi\n";

    write(0, buf, 10);

    char readbuf[1024] = {0};
    // read(1, readbuf, 10); works too
    read(2, readbuf, 10);
    write(2, readbuf, 10);

    return 0;
}

output:

abcdefghi
hey stdin  <-- I input this
hey stdin

Confused, I thought it should be an error.

Experiement:

Then I tried redirecting fd 2.

$ ./a.out 2>/dev/null

this time both the read and 2nd write is not 'visible'. The output is

abcdefgi

So the stderr can be used for read?

I then close the stdout & stderr and make two copy of stdin:

int main(int argc, char** argv){
    char buf[1024] = "abcdefghi\n";

    close(1);
    close(2);

    dup2(0, 1);
    dup2(0, 2);

    write(0, buf, 10);

    char redbuf[1024] = {0};
    read(2, redbuf, 10);
    write(2, redbuf, 10);

    return 0;
}

Again it works.

output:

abcdefghi
hey stdin  <-- I input this
hey stdin

So stdin can be used for write?

I need some explanation here.

Question

I want to know:

Why stdout/stderr can be used for read?

Why stdin can be used for write?

Is the three stream(stdin,stdout,stderr) internally one stream ?

If not, why am I getting this result?

2
  • 1
    Some general tips: (1) If you’re trying to figure out what’s happening, look at your return codes!  It probably won’t actually help in this case, but failure to check return codes is sloppy programming.  (2) If you’re writing information to a particular file descriptor, and then (in the course of debugging / troubleshooting) you redirect that file descriptor to /dev/null, consider using the exit code from your program to pass one byte’s worth of status information back to the user (you).  … (Cont’d) Commented Aug 26, 2018 at 19:43
  • (Cont’d) … A specific tip for this situation: try running your program with input and/or output redirected to files that you have read/write access to (where you can provide initial contents and look at the contents afterwards). Commented Aug 26, 2018 at 19:43

2 Answers 2

1

It is only convention to use fd 0/1/2 for input/output/error. If you call a program without redirection, all three refer to your tty, and your tty is open with read and write access. That means you can read or write to them as you want. You could call them the same stream, although the expression stream is often used for higher level I/O such as FILE in C or stream in C++.

This is the reason why both or not redirected examples just echo the text you enter.

On the other hand, if you do redirection, the shell will open the files with read only or write only access. In your example ./a.out 2>/dev/null, the write to 0 is still connected to the terminal because it is not redirected, and therefor is displayed on the screen. The read from 2 is connected to a write only /dev/null and should therefor fail, but you wouldn't notice the difference from your program. The write to 2 succeeds, but is written to /dev/null. The invalid read and valid write to /dev/null are not visible on your terminal.

-1

As long as you did not redirect stdin/stdout/stderr, these filedescriptors heve been opened by the login procedure and that opens the related tty for read and write as the first file (resulting in file descriptor #0) and later calls dup() 2 times to get the file descriptors for stdout and stderr.

As mentioned in How does `less` take data from stdin while still be able to read commands from user? in former times (before /dev/tty has been introduced to UNIX) programs like more did read from stderr when asking for confirmation.

Not the answer you're looking for? Browse other questions tagged .