Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integer File Descriptor "0" in open()

In Python 3, it is possible to open a file object using an "integer file descriptor" with the format:

stdout = open(1, "w")
stdout.write("Hello World") # Prints Hello World
stdout.close()

Though, interestingly, I found that 0 is also a valid stream.

If I put this in the file testio.py:

stdout = open(0, "w")
stdout.write("Foo Bar\n")
stdout.close()

And then run that code the output is:

bash-3.2$ python3 testio.py
Foo Bar

Which seems just like stdout. However...

bash-3.2$ python3 testio.py > testio.txt
Foo Bar
bash-3.2$ cat testio.txt

So it seems that this is actually not stdout, but something else. And it does not appear to be stderr either:

bash-3.2$ python3 testio.py 2> testio.txt
Foo Bar
bash-3.2$ cat testio.txt

However, I did find that the output can be redirected using 0>:

bash-3.2$ python3 testio.py 0> testio.txt
bash-3.2$ cat testio.txt
Foo Bar

So my question is, what exactly does open(0, "w") due? And what is this "0>" stream that is being redirected?

Python 3.6.5
Bash 3.2

like image 930
The Matt Avatar asked Dec 22 '18 18:12

The Matt


People also ask

What is the 0 file descriptor?

At the file descriptor level, stdin is defined to be file descriptor 0, stdout is defined to be file descriptor 1; and stderr is defined to be file descriptor 2.

What does open 0 do in Python?

So 0 is a valid file descriptor, but one that is your stdin , which you're opening again for writing.

Is 0 a valid file descriptor?

0 is a valid file descriptor, but it usually refers to stdin.

What is an open file descriptor?

A file descriptor is a number that uniquely identifies an open file in a computer's operating system. It describes a data resource, and how that resource may be accessed. When a program asks to open a file — or another data resource, like a network socket — the kernel: Grants access.


2 Answers

No file descriptor (FD) number is special. stdin on FD 0, stdout on FD 1 and stderr on FD 2 is just a convention.

When you log in, the associated terminal device will be "connected" to these FDs. When you run a command, it inherits the descriptors unless you instruct the shell to make redirections. But once the program starts, you can close, dup, or open FDs as you like.

Back to your question:

stdout = open(0, "w")
stdout.write("Hello World") # Prints Hello World
stdout.close()

Despite the name, open does not open anything in this case. It creates a Python file object (with buffers and all high level stuff) from an already open low-level FD which is really just a number (an index to a table of open files in the kernel). There was a separate function for it: os.fdopen

Little bit more interesting is that there is no standard way to change the open mode from read to write and your program writes to std input. The answer is (at least on Linux) that this is not happening at all. As you can see with lsof, all 3 standard FDs are normally open in read/write mode (marked by trailing u), e.g.:

cmd    32154 user    0u      CHR  136,7       0t0        10 /dev/pts/7
cmd    32154 user    1u      CHR  136,7       0t0        10 /dev/pts/7
cmd    32154 user    2u      CHR  136,7       0t0        10 /dev/pts/7

So your program just writes to the FD 0 which is connected to the terminal.

like image 67
VPfB Avatar answered Sep 25 '22 20:09

VPfB


File handle 0 is stdin. Without redirection stdout, stderr, and stdin are all pointing to the terminal (so will all act the same). However when redirection is used, they will behave differently, because they will no longer be the same.

I.E. If you do python3 testio.py 2> testio.txt, then stdout goes to the file, but stdin is still the terminal.

This is just a bi-product of there being no checking to see that you only read stdin, and only write stdout, and stderr.

like image 43
ctrl-alt-delor Avatar answered Sep 24 '22 20:09

ctrl-alt-delor