2010-10-22 10 views
5

स्पष्ट रूप से यह लगभग Bad pipe filedescriptor when reading from stdin in python - Stack Overflow "का डुप्लिकेट है; हालांकि, मेरा मानना ​​है कि यह मामला थोड़ा अधिक जटिल है (और यह विंडोज विशिष्ट नहीं है, क्योंकि उस धागे का निष्कर्ष था)।लिनक्स: पायथन में पाइप (एनसीआरएसईएस) स्क्रिप्ट, stdin और टर्मियो

मैं वर्तमान में पाइथन में एक साधारण स्क्रिप्ट के साथ प्रयोग करने की कोशिश कर रहा हूं: मैं स्क्रिप्ट में इनपुट आपूर्ति करना चाहता हूं - या तो कमांड लाइन तर्कों के माध्यम से; या इस स्क्रिप्ट के लिए एक स्ट्रिंग को 'पाइप' करके - और स्क्रिप्ट को 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 
# ################ 

जहां तक ​​मैं देख सकता हूँ, मुद्दा है: - जब भी अजगर स्क्रिप्ट में हम पाइप तार, अजगर स्क्रिप्ट संदर्भ टर्मिनल करने के लिए stdin और सूचनाओं की के रूप में खो देता है कि प्रतिस्थापित stdintermios संरचना अब और नहीं है - और stdin अब टर्मिनल नहीं है, curses.initscr() कुछ भी प्रस्तुत किए बिना तत्काल बाहर निकलता है।

तो, मेरी सवाल है - संक्षेप में: मैं किसी भी तरह प्राप्त कर सकते हैं, कि वाक्य रचना echo "blabla" | ./testcurses.py -curses में पाइप स्ट्रिंग दिखा समाप्त होता है? अधिक विशेष रूप से: क्या यह स्क्रिप्ट एक पाइथन स्क्रिप्ट से कॉलिंग टर्मिनल के stdin के संदर्भ को पुनर्प्राप्त करना संभव है, भले ही यह स्क्रिप्ट "पाइप" हो रही हो?

किसी भी संकेत दिए गए के लिए अग्रिम धन्यवाद,

चीयर्स!

 

 

पुनश्च: 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 ..) 

उत्तर

1

यह नहीं माता पिता प्रक्रिया शामिल हो रही बिना किया जा सकता।

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

stdin के साथ एक subshell में पाइप foopipe.py करने के लिए फ़ाइल वर्णनकर्ता 3. में दोहराया जाएगा कि अब सब हम क्या करने की जरूरत उपयोग है कि हमारे माता पिता से अतिरिक्त मदद है: सौभाग्य से, वहाँ एक रास्ता bash शामिल पाने के लिए I/O redirection का उपयोग कर अजगर स्क्रिप्ट में प्रक्रिया (के बाद से हम एफडी 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 है तो आपकी समस्या हल हो जाती है।

+0

धन्यवाद, महोदय, संक्षिप्त और कामकाजी उत्तर के लिए! :) मैं वास्तव में 'बाश' का उपयोग करता हूं, क्योंकि मैं उबंटू ल्यूसिड पर हूं। मेरा उदाहरण, आपके परिवर्तनों के साथ अपडेट किया गया है, [testcurses-stdin.py] के रूप में पाया जा सकता है (http://sdaaubckp.svn.sourceforge.net/viewvc/sdaaubckp/single-scripts/testcurses-stdin.py?revision=75&content- type = पाठ% 2Fplain और pathrev = 75); और इसे ''(echo" blabla "|/testcurses-stdin.py -) 3 <& 0'' ... – sdaau

+0

_PS के साथ बुलाया जाना चाहिए: मुझे स्वीकार करना होगा कि मैंने [I/O पुनर्निर्देशन] देखा है (http: //www.faqs.org/docs/abs/HTML/io-redirection.html) सैकड़ों बार - मैंने इसे पोस्ट करने से पहले - और यह हमेशा मुझे भ्रमित करने के समाप्त होता है; मुझे उचित समाधान का पता लगाना मुश्किल था। साथ ही, जैसा कि मैं एक-लाइनर का बहुत शौकिया हूं, "कमांड लाइन पर बदसूरत वाक्यविन्यास" वास्तव में ** अधिक ** सराहना की गई है - मुझे जो चीजें पसंद नहीं हैं उनमें से एक चल रहा है '' exec 3 < & 0' 'कुछ ऐसा करने से पहले, अनिवार्य रूप से, एक liner_। उत्तर के लिए फिर से धन्यवाद, Frédéric - और चीयर्स! – sdaau

8
समस्या यह है कि जब भी मैं वास्तविक पाइपिंग का प्रयास करता हूं, तो यह गड़बड़ लगता है, और शाप खिड़की कभी नहीं दिखाती है। [... स्निप ...] जहाँ तक मैं देख सकता हूं, मुद्दा यह है: - जब भी हम पाइथन लिपि में पाइप स्ट्रिंग करते हैं, तो पाइथन लिपि टर्मिनल के संदर्भ को स्टडीन के रूप में खो देता है, और नोटिस करता है कि प्रतिस्थापित स्टडीन टर्मियोस स्ट्रक्चर नहीं है - और चूंकि stdin अब टर्मिनल नहीं है, curses.initscr() कुछ भी प्रस्तुत किए बिना तत्काल बाहर निकलता है।

असल में, शाप विंडो दिखाती है, लेकिन चूंकि आपके बहादुर नए stdin, myscreen.getch() पर तुरंत कोई इनपुट नहीं है। इसलिए शाप परीक्षण के साथ इसका कोई लेना-देना नहीं है कि stdin टर्मिनल है।

तो यदि आप myscreen.getch() और अन्य शाप इनपुट फ़ंक्शंस का उपयोग करना चाहते हैं, तो आपको अपने टर्मिनल को फिर से खोलना होगा। लिनक्स और * निक्स सिस्टम पर आमतौर पर /dev/tty नामक डिवाइस होता है जो वर्तमान टर्मिनल को संदर्भित करता है। तो आप कुछ ऐसा कर सकते हैं:

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

myscreen.getch() पर कॉल करने से पहले।

+0

धन्यवाद, निंजाल, अच्छी व्याख्या के लिए - यह मुझे बेहतर समझने में मदद करता है कि पाइपिंग और मानक I/O कैसे काम करता है! बीटीडब्ल्यू, मैं वास्तव में 'myscreen.getch() '' का उपयोग करने में रूचि नहीं रखता - मैं जो करना चाहता था, वह इस स्क्रिप्ट में पाइप कच्चा 'अनौपचारिक' डेटा है, और स्क्रिप्ट डेटा को पार्स करती है, और इसे स्क्रीन पर प्रारूपित करती है 'ncurses' का उपयोग करके," रीयल-टाइम "(_ जो विभिन्न समस्याओं का एक पूरा सेट है - लेकिन 'stdin' के डुप्लिकेशंस की आवश्यकता को समझना एक वास्तविक शो स्टॉपर_ था)। चीयर्स! – sdaau

+1

मजाकिया बात यह है कि यदि आप स्क्रिप्ट को अनिश्चित काल तक चल रहे हैं और 'myscreen.getch()' का उपयोग नहीं करते हैं, तो आपके द्वारा पोस्ट की गई स्क्रिप्ट पहले से ही काम करती है, यह ध्यान देने के लिए बस बहुत तेज है। – ninjalj

+0

पीएस: बस कहना चाहता था कि मैंने [testcurses-stdin.py] अपडेट किया है (http://sdaaubckp.svn.sourceforge.net/viewvc/sdaaubckp/single-scripts/testcurses-stdin.py?revision=76&content-type= पाठ% 2 फ्लाइन और पथरेव = 75) इसलिए यह 'fd3' के बजाय'/dev/tty' को डुप्लिकेट करता है - और अब स्क्रिप्ट को 'echo "blabla के साथ स्पष्ट रूप से बुलाया जा सकता है" | ./testcurses-stdin.py -' '। धन्यवाद, निंजाल - और चीयर्स! – sdaau

संबंधित मुद्दे