-2

So I've entered two lines of code into a Python shell to read from STDOUT and write to STDIN:

>>> writetoinput=open("/dev/stdin","w")
>>> readfromoutput=open("/dev/stdout")

and unexpected things happen when you do file operations on them.

First, I tried writing to STDIN:

>>> writetoinput.write("banana?")

But then, when I called input(), the terminal did not return "banana?" and instead prompted for user input.

Then, I ran print("hi") and tried reading from STDOUT:

>>> readfromoutput.read()

Instead of printing "hi", the terminal hanged, and I had to do a Keyboard Interrupt.

Question

Why does Python behave this way, especially with the fact that reading STDOUT hangs the terminal?

  • I ma not sure you can write to FD0 of a program from within said program. – Itération 122442 Jul 11 '23 at 14:09
  • Not one of the downvoters, but some people may be finding this question not useful since it's hard to tell at a glance what's being asked and what sort of information is going to be in the answers. If you rewrote and retitled it to be about a specific, searchable problem like "Why do writes to /dev/stdin not appear in input()" or "Why does reading /dev/stdout hang?", then it would likely receive much more positive attention. – Brian61354270 Jul 11 '23 at 14:58

2 Answers2

1

especially with the fact that reading STDOUT hangs the terminal

It didn't "hang the terminal". It was trying to read input from the terminal, just like you told it to.

.read() will read ALL of the available input. It doesn't stop at just one line, unlike the input() function.

It was waiting until the input was completely exhausted. When reading from a unix interactive terminal, the way to signal "end of input" is to type Ctrl-D at the beginning of a line.

John Gordon
  • 29,573
  • 7
  • 33
  • 58
1

This is not python behaviour, this is system behaviour. Those are not literal "files", they are special files that represent a more abstract concept (just like devices are unix "files").

Instead of hitting ctrl+c you should've hit ctrl+d to send end-of-file and then you'd see your output! Why?

Because unless redirected, stdin and stdout are just bound to the console (tty). All three are the same thing by default because both stdout and stderr are printed directly to console and stdin is always from the console:

➜ ll /dev/std*
lrwxrwxrwx 1 root root 15 cze 27 08:41 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 cze 27 08:41 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 cze 27 08:41 /dev/stdout -> /proc/self/fd/1
➜ ll /proc/self/fd
total 0
dr-x------ 2 ev ev  0 lip 11 16:14 ./
dr-xr-xr-x 9 ev ev  0 lip 11 16:14 ../
lrwx------ 1 ev ev 64 lip 11 16:14 0 -> /dev/pts/1
lrwx------ 1 ev ev 64 lip 11 16:14 1 -> /dev/pts/1
lrwx------ 1 ev ev 64 lip 11 16:14 2 -> /dev/pts/1

As you can see, the /dev/std* points always to current process' file descriptors 0, 1, 2.

In case of console, those 3 are just that console's tty.

So when you "open" stdout for reading, you're doing the same thing as stdin does - wait for all stuff until EOF is received.

>>> b = open("/dev/stdout")
>>> b.read()
this looks bad, right?
but see this
let's hit ctrl+d
"this looks bad, right?\nbut see this\nlet's hit ctrl+d\n"
>>> 
>>> 
>>> # same as
>>> sys.stdin.read()
see?
like this
'see?\nlike this\n'

How writing is done isn't clear to you because python usually skips the concept of flushing your buffer. When you close a file, the buffer gets flushed, so you don't need to do it manually. And when using print the flushing is not guaranteed (defaults to flush=False), but usually happens anyways.

>>> a = open("/dev/stdin",'w')
>>> a.write("this is nice")
12
>>> a.flush()
this is nice>>> 

Now, with flushing, we can actually see what we did! sys.stdout flushes for me automatically, so when I do sys.stdout.write the content+saved bytes are on the same line, but it's the same.

h4z3
  • 5,265
  • 1
  • 15
  • 29