Aparentemente, esto es casi un duplicado de "Bad pipe filedescriptor when reading from stdin in python - Stack Overflow"; sin embargo, creo que este caso es un poco más complicado (y no es específico de Windows, ya que la conclusión de ese hilo fue).Linux: ingrese a la secuencia de comandos de Python (ncurses), stdin y termios
Actualmente estoy tratando de experimentar con un script simple en Python: me gustaría proporcionar entradas para el script, ya sea a través de argumentos de línea de comando; o "pipeando" una cadena a este script - y haga que el script muestre esta cadena de entrada usando una interfaz de terminal curses
.
El guión completo, aquí llamado testcurses.py
, se da a continuación. El problema es que cada vez que pruebo la tubería real, eso parece arruinar stdin, y la ventana curses
nunca se muestra. Aquí es un terminal de salida:
## 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
# ################
Por lo que yo puedo ver, la cuestión es: - Cada vez que las cadenas de tubería en la secuencia de comandos de Python, la secuencia de comandos de Python pierde la referencia al terminalcomo stdin
, y las comunicaciones que el stdin
reemplazado ya no es una estructura termios
- y dado que stdin
ya no es un terminal, curses.initscr()
sale inmediatamente sin procesar nada.
lo tanto, mi pregunta es - en pocas palabras: ¿hay algún modo de lograr, que la sintaxis echo "blabla" | ./testcurses.py -
termina mostrando la cadena canalizado en curses
? Más específicamente: ¿es posible recuperar una referencia al stdin
del terminal que realiza la llamada desde un script de Python, incluso si este script está siendo "canalizado"?
Gracias de antemano por cualquier punteros,
Saludos!
PD: el guión 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 ..)
Gracias, señor, por la respuesta concisa y operativa. :) De hecho, uso 'bash', ya que estoy en Ubuntu Lucid. Mi ejemplo, actualizado con sus cambios, se puede encontrar como [testcurses-stdin.py] (http://sdaaubckp.svn.sourceforge.net/viewvc/sdaaubckp/single-scripts/testcurses-stdin.py?revision=75&content- type = text% 2Fplain & pathrev = 75); y debería llamarse con ''(echo" blabla "| ./testcurses-stdin.py -) 3 <& 0'' ... – sdaau
_PS: Debo admitir que he visto [redirección de E/S] (http: //www.faqs.org/docs/abs/HTML/io-redirection.html) cientos de veces, también antes de publicar esto, y siempre termina confundiéndome; Realmente me hubiera costado encontrar la solución adecuada. Además, como soy muy aficionado a los one-liners, la "' sintaxis fea en la línea de comando' "es en realidad ** más ** apreciada, una de las cosas que no me gusta es ejecutar '' 'exec 3 < & 0' 'antes de ejecutar algo que es, esencialmente, un trazador de líneas_. Gracias de nuevo por la respuesta, Frédéric, ¡y aplausos! – sdaau