Linux:pipe入Python(ncurses)脚本,stdin和termios

显然,这几乎是“ 糟糕的pipe道filedescriptor从Python中的标准input读取时重复- Stack Overflow ”; 不过,我认为这个情况稍微复杂一些( 而且这不是Windows特有的,因为这个线程的结论是 )。

我正在尝试在Python中使用一个简单的脚本:我想为脚本提供input – 通过命令行参数; 或者通过将一个string“pipe”到这个脚本中 – 并使脚本使用cursesterminal接口显示这个inputstring。

完整的脚本,在这里被称为testcurses.py ,如下所示。 问题是,每当我尝试实际的pipe道,似乎搞乱标准input, curses窗口永远不会显示。 这是一个terminal输出:

 ## CASE 1: THROUGH COMMAND LINE ARGUMENT (arg being stdin): ## $ ./testcurses.py - ['-'] 1 stdout/stdin (obj): <open file '<stdout>', mode 'w' at 0xb77dc078> <open file '<stdin>', mode 'r' at 0xb77dc020> stdout/stdin (fn): 1 0 env(TERM): xterm xterm stdin_termios_attr [27906, 5, 1215, 35387, 15, 15, ['\x03', ... '\x00']] stdout_termios_attr [27906, 5, 1215, 35387, 15, 15, ['\x03', ... '\x00']] opening - obj <open file '<stdin>', mode 'r' at 0xb77dc020> TYPING blabla HERE wr TYPING blabla HERE at end before curses TYPING blabla HERE # # AT THIS POINT: # in this case, curses window is shown, with the text 'TYPING blabla HERE' # ################ ## CASE 2: THROUGH PIPE ## ## NOTE I get the same output, even if I try syntax as in SO1057638, like: ## python -c "print 'TYPING blabla HERE'" | python testcurses.py - ## $ echo "TYPING blabla HERE" | ./testcurses.py - ['-'] 1 stdout/stdin (obj): <open file '<stdout>', mode 'w' at 0xb774a078> <open file '<stdin>', mode 'r' at 0xb774a020> stdout/stdin (fn): 1 0 env(TERM): xterm xterm stdin_termios_attr <class 'termios.error'>::(22, 'Invalid argument') stdout_termios_attr [27906, 5, 1215, 35387, 15, 15, ['\x03', '\x1c', '\x7f', '\x15', '\x04', '\x00', '\x01', '\xff', '\x11', '\x13', '\x1a', '\xff', '\x12', '\x0f', '\x17', '\x16', '\xff', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00']] opening - obj <open file '<stdin>', mode 'r' at 0xb774a020> wr TYPING blabla HERE at end before curses TYPING blabla HERE # # AT THIS POINT: # script simply exits, nothing is shown # ################ 

据我所见,问题是: – 每当我们将stringinput到Python脚本中时,Python脚本就会丢失对stdin的引用,并且注意到被replace的stdin不再是termios结构 – 而且由于stdin是不再是一个terminal, curses.initscr()立即退出而不呈现任何东西。

所以,我的问题是 – 简而言之:我可以以某种方式实现,即语法echo "blabla" | ./testcurses.py - echo "blabla" | ./testcurses.py -结束了在curses显示pipe道的string? 更具体地说:是否有可能从Python脚本中检索对主叫terminal的stdin的引用,即使这个脚本被“传送”到了?

提前感谢任何指针,

干杯!

PS: testcurses.py脚本:

 #!/usr/bin/env python # http://www.tuxradar.com/content/code-project-build-ncurses-ui-python # http://diveintopython.net/scripts_and_streams/stdin_stdout_stderr.html # http://bytes.com/topic/python/answers/42283-curses-disable-readline-replace-stdin # # NOTE: press 'q' to exit curses - Ctrl-C will screw up yer terminal # ./testcurses.py "blabla" # works fine (curseswin shows) # ./testcurses.py - # works fine, (type, enter, curseswins shows): # echo "blabla" | ./testcurses.py "sdsd" # fails to raise curses window # # NOTE: when without pipe: termios.tcgetattr(sys.__stdin__.fileno()): [27906, 5, 1215, 35387, 15, 15, ['\x03', # NOTE: when with pipe | : termios.tcgetattr(sys.__stdin__.fileno()): termios.error: (22, 'Invalid argument') import curses import sys import os import atexit import termios def openAnything(source): """URI, filename, or string --> stream http://diveintopython.net/xml_processing/index.html#kgp.divein This function lets you define parsers that take any input source (URL, pathname to local or network file, or actual data as a string) and deal with it in a uniform manner. Returned object is guaranteed to have all the basic stdio read methods (read, readline, readlines). Just .close() the object when you're done with it. """ if hasattr(source, "read"): return source if source == '-': import sys return sys.stdin # try to open with urllib (if source is http, ftp, or file URL) import urllib try: return urllib.urlopen(source) except (IOError, OSError): pass # try to open with native open function (if source is pathname) try: return open(source) except (IOError, OSError): pass # treat source as string import StringIO return StringIO.StringIO(str(source)) def main(argv): print argv, len(argv) print "stdout/stdin (obj):", sys.__stdout__, sys.__stdin__ print "stdout/stdin (fn):", sys.__stdout__.fileno(), sys.__stdin__.fileno() print "env(TERM):", os.environ.get('TERM'), os.environ.get("TERM", "unknown") stdin_term_attr = 0 stdout_term_attr = 0 try: stdin_term_attr = termios.tcgetattr(sys.__stdin__.fileno()) except: stdin_term_attr = "%s::%s" % (sys.exc_info()[0], sys.exc_info()[1]) try: stdout_term_attr = termios.tcgetattr(sys.__stdout__.fileno()) except: stdout_term_attr = `sys.exc_info()[0]` + "::" + `sys.exc_info()[1]` print "stdin_termios_attr", stdin_term_attr print "stdout_termios_attr", stdout_term_attr fname = "" if len(argv): fname = argv[0] writetxt = "Python curses in action!" if fname != "": print "opening", fname fobj = openAnything(fname) print "obj", fobj writetxt = fobj.readline(100) # max 100 chars read print "wr", writetxt fobj.close() print "at end" sys.stderr.write("before ") print "curses", writetxt try: myscreen = curses.initscr() #~ atexit.register(curses.endwin) except: print "Unexpected error:", sys.exc_info()[0] sys.stderr.write("after initscr") # this won't show, even if curseswin runs fine myscreen.border(0) myscreen.addstr(12, 25, writetxt) myscreen.refresh() myscreen.getch() #~ curses.endwin() atexit.register(curses.endwin) sys.stderr.write("after end") # this won't show, even if curseswin runs fine # run the main function - with arguments passed to script: if __name__ == "__main__": main(sys.argv[1:]) sys.stderr.write("after main1") # these won't show either, sys.stderr.write("after main2") # (.. even if curseswin runs fine ..) 

Solutions Collecting From Web of "Linux:pipe入Python(ncurses)脚本,stdin和termios"

没有涉及到父进程,这是无法完成的。 幸运的是,有一种方法可以使用I / O重定向来获取bash :

 $ (echo "foo" | ./pipe.py) 3<&0 

这将通过stdin复制到文件描述符3中将foo pipe.py给子文件中的pipe.py现在我们需要做的就是在Python脚本中使用父进程的额外帮助(因为我们将继承fd 3):

 #!/usr/bin/env python import sys, os import curses output = sys.stdin.readline(100) # We're finished with stdin. Duplicate inherited fd 3, # which contains a duplicate of the parent process' stdin, # into our stdin, at the OS level (assigning os.fdopen(3) # to sys.stdin or sys.__stdin__ does not work). os.dup2(3, 0) # Now curses can initialize. screen = curses.initscr() screen.border(0) screen.addstr(12, 25, output) screen.refresh() screen.getch() curses.endwin() 

最后,您可以通过首先运行子shell来解决命令行中难看的语法:

 $ exec 3<&0 # spawn subshell $ echo "foo" | ./pipe.py # works $ echo "bar" | ./pipe.py # still works 

这解决了你的问题,如果你有bash

问题是,每当我尝试实际的管道,这似乎搞乱标准输入,诅咒窗口永远不会显示。 [… snip …]据我所见,问题是: – 每当我们将字符串输入到Python脚本中时,Python脚本就会丢失对stdin的引用,并且注意到被替换的stdin不是一个termios结构 – 而且由于stdin不再是一个终端,curses.initscr()立即退出而不呈现任何东西。

实际上,curses窗口确实显示,但由于你的勇敢的新stdin没有更多的输入, myscreen.getch()立即返回。 所以这与curses测试stdin是否是终端无关。

所以如果你想使用myscreen.getch()和其他的curses输入函数,你将不得不重新打开你的终端。 在Linux和* nix系统上,通常会有一个名为/dev/tty的设备引用当前终端。 所以你可以做这样的事情:

 f=open("/dev/tty") os.dup2(f.fileno(), 0) 

在你调用myscreen.getch()