Linux:管道进入Python(ncurses)脚本,stdin和termios
显然这几乎是“
Bad pipe filedescriptor when reading from stdin in python – Stack Overflow”的重复;但是,我认为这种情况稍微复杂一些(并且它不是Windows特定的,因为该线程的结论是).
我目前正在尝试用Python编写一个简单的脚本:我想为脚本提供输入 – 通过命令行参数;或者通过“管道”字符串到此脚本 – 并让脚本使用curses终端接口显示此输入字符串. 完整的脚本,这里称为testcurses.py,如下所示.问题是每当我尝试实际的管道时,这似乎搞乱了stdin,而curses窗口从未显示过.这是一个终端输出: ## 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,['x03',... 'x00']] stdout_termios_attr [27906,... '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,'x1c','x7f','x15','x04','x00','x01','xff','x11','x13','x1a','x12','x0f','x17','x16','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 # ################ 据我所知,问题是: – 每当我们将字符串传递给Python脚本时,Python脚本就会失去对终端的引用作为stdin,并注意到被替换的stdin不再是termios结构 – 并且因为stdin是不再是终端,curses.initscr()立即退出而不渲染任何东西. 所以,我的问题是 – 简而言之:我可以以某种方式实现,语法echo“blabla”| ./testcurses.py – 最终在curses中显示管道字符串?更具体地说:是否可以从Python脚本中检索对调用终端的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,# 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 ..) 解决方法
如果不涉及父进程,则无法完成此操作.幸运的是,有一种方法可以使用
I/O redirection获得
bash:
$(echo "foo" | ./pipe.py) 3<&0 这会将foo管道传递到子shell中的pipe.py,并将stdin复制到文件描述符3.现在我们需要做的就是在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,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,那就解决了你的问题. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |