Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
575 views
in Technique[技术] by (71.8m points)

linux - Python in raw mode stdin print adds spaces

I needed to switch the standard input to non-buffered mode in Python, so that I can read single characters off it. I managed to get it working, but now the standard output is broken: somehow it seems like after the newline character, some space characters are emitted, zero on the first line, 3 on the second, 6 on the third, etc, like this:

ASD
   ASD
      ASD

Operating system is Ubuntu Linux 12.04, 64-bit edition, Python version is 3.2.3.

How can I rid myself from this behavior?

Below is the code I've used:

import sys
import tty
import termios

fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
tty.setraw(sys.stdin)

for i in range(0, 10):
    print("ASD")

termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem you're experiencing is the difference between 'raw', 'cooked', and 'cbreak' modes. And these modes are modes of the kernel level terminal driver, not modes of your application code or the standard library or anything else in userspace. This is the old-school Unix way of referring to these. Posix has replaced them with a much more fine-grained set of attributes, though the Posix attributes are typically flipped in concert with helper functions in a way that mimics the old 'raw', 'cooked' and 'cbreak' modes.

In cooked mode, the terminal driver itself has primitive line editing functionality builtin. It handles backspace, word erase (basically backspace a whole word at once) and similar things. Nothing sophisticated like handling arrow keys or history or anything like that. Very primitive. In this mode, your program never sees anything from the terminal until the end-of-line (eol) character is sent, and then your program gets a whole line, and the line ending is translated to Unix standard regardless of what the terminal actually does. Also, as part of this, the terminal driver echoes typed characters back to the terminal so the user can see what they're typing.

In 'cooked' mode, the kernel level terminal driver also does some output translation. And part of that is turning into if needed.

Also, in 'cooked' mode the terminal driver handles special characters like Control-C (sends a SIGINT to the controlling process group (translated by CPython into a KeyboardInterrupt exception)) and Control-Z (sends a SIGTSTP (like a SIGSTOP, but can be caught) to the controlling process group).

In 'cbreak' mode, line editing is no longer done. The terminal driver gives each character (or short character sequence, like the escape sequence for an arrow key) to the program immediately. These characters are not echoed to the screen, and so unless your program then prints them the user won't see them. The terminal driver though still handles special characters like Control-C and Control-Z, though it ceases to handle line editing characters like backspace or the word-erase character (typically Control-W). Also, some output processing is still done, so the driver turns a into a .

In 'raw' mode, no processing is done on either input or output. No special character handling, no echoing, no transforming into , no handling for Control-Z, nothing. It's up to the program that puts the terminal in raw mode to do it all.

Now, you are setting the attributes for sys.stdin so you may think this shouldn't affect sys.stdout. But, in fact, both of your file descriptors lead to the exact same 'instance' of a terminal driver. And it's the settings for the terminal driver that determine what happens. So it doesn't matter if you change those settings through sys.stdin, sys.stdout, or even sys.stderr, all change the same underlying terminal driver instance and they affect all the others.

This, of course, is not true of file descriptors that have been redirected by the shell before your program is launched.

As a side note, you can use the stty -a on the command line to see a full read out of all of these flags (including which control characters result in which signals in cooked and cbreak modes).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...