代码之家  ›  专栏  ›  技术社区  ›  sdaau

Linux:导入Python(ncurses)脚本、stdin和termios

  •  5
  • sdaau  · 技术社区  · 14 年前

    显然这几乎是 Bad pipe filedescriptor when reading from stdin in python - Stack Overflow “不过,我认为这个案子稍微复杂一些( 而且它不是Windows特有的,因为该线程的结论是 ).

    curses 终端接口。

    完整的剧本,这里叫做 testcurses.py ,如下所示。问题是,每当我尝试真正的管道,这似乎弄乱了stdin,和 窗口永远不会出现。这是一个终端输出:

    ## 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 
    # ################
    

    终端 作为 stdin 标准物质 不是 termios 标准物质 不再是终点站, curses.initscr()

    所以,我的 问题 简而言之:我能不能实现 echo "blabla" | ./testcurses.py - 诅咒 ? 更具体地说:是否可以检索对调用终端的引用 标准物质 从一个Python脚本,即使这个脚本是“管道”到?

    提前谢谢你的指点,

    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 ..)
    
    2 回复  |  直到 8 年前
        1
  •  1
  •   Frédéric Hamidi    14 年前

    如果不涉及父进程,就无法完成此操作。幸运的是,有办法 bash 涉及使用 I/O redirection :

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

    会管用的 foo 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, 25, output)
    screen.refresh()
    screen.getch()
    curses.endwin()
    

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

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

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

        2
  •  10
  •   ninjalj    14 年前
    问题是,每当我尝试实际的管道,这似乎弄乱了stdin,诅咒窗口永远不会显示。 [剪…]

    实际上,“诅咒”窗口确实会显示,但由于没有更多关于您的勇敢的新 标准物质 , myscreen.getch() 立即返回。所以这与诅咒无关 标准物质

    所以如果你想用 myscreen.getch() /dev/tty 指的是当前终端。所以你可以这样做:

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

    在你打电话给 .

    推荐文章