Linux:通过管道输入 Python (ncurses) 脚本、stdin 和 termios
显然,这几乎是“读取时错误的管道文件描述符”的重复python 中的标准输入 - 堆栈内存溢出”;但是,我认为这种情况稍微复杂一些(并且它不是 Windows 特定的,因为该线程的结论是)。
我目前正在尝试用 Python 尝试一个简单的脚本:我想为脚本提供输入 - 通过命令行参数;或者通过“管道”将字符串传递到此脚本 - 并让脚本使用 curses
终端界面显示此输入字符串。
下面给出了完整的脚本,此处称为 testcurses.py
。问题是,每当我尝试实际的管道时,这似乎都会弄乱标准输入,并且 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, 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
# ################
据我所知,问题是: - 每当我们将字符串通过管道传输到 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, 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 ..)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
实际上,curses 窗口确实显示了,但是由于您勇敢的新 stdin 上没有更多输入,
myscreen.getch()
立即返回。所以它与curses测试stdin是否是终端无关。因此,如果您想使用 myscreen.getch() 和其他 Curses 输入函数,您必须重新打开终端。在 Linux 和 *nix 系统上,通常有一个名为
/dev/tty
的设备,它引用当前终端。因此,您可以执行以下操作:在调用
myscreen.getch()
之前。Actually, the curses window does show, but since there is no more input on your brave new stdin,
myscreen.getch()
returns immediately. So it has nothing to do with curses testing whether stdin is a terminal.So if you want to use
myscreen.getch()
and other curses input functions, you'll have to reopen your terminal. On Linux and *nix systems there is usually a device called/dev/tty
that refers to the current terminal. So you can do something like:before your call to
myscreen.getch()
.如果没有父进程的参与,这是无法完成的。幸运的是,有一种方法可以使用 bash 参与进来http://www.faqs.org/docs/abs/HTML/io-redirection.html" rel="nofollow">I/O 重定向 :
这会将
foo
传送到 < code>pipe.py 位于子 shell 中,并将stdin
复制到文件描述符 3 中。现在我们需要做的就是在 python 脚本中使用来自父进程的额外帮助(因为我们' ll继承fd 3):最后,您可以通过首先运行子shell来解决命令行上丑陋的语法:
如果您有
bash
,这可以解决您的问题。This can't be done without getting the parent process involved. Fortunately, there's a way to get bash involved using I/O redirection :
That will pipe
foo
topipe.py
in a subshell withstdin
duplicated into file descriptor 3. Now all we need to do is use that extra help from our parent process in the python script (since we'll inherit fd 3):Finally, you can work around the ugly syntax on the command line by running the subshell first:
That solves your problem, if you have
bash
.