2017-04-08 8 views
6

पर रीडायरेक्ट किया गया है, मैं पाइथन से एक प्रोग्राम कॉल करना चाहता हूं और इसे मानता हूं कि stdout एक tty है जब भी पाइथन की प्रक्रिया stdout एक पाइप से जुड़ा हुआ है। इसलिए मैं प्राप्त करने के लिए है कि, जहां से सत्यापित किया जा सकता pty.spawn फ़ंक्शन का उपयोग किया है:पायथन pty.spawn stdin प्रतिध्वनि नहीं किया गया है लेकिन मास्टर के stdout

$ python -c "import sys; from subprocess import call; call(sys.argv[1:])" python -c "import sys; print sys.stdout.isatty()" | cat 
False 

$ python -c "import sys; import pty; pty.spawn(sys.argv[1:])" python -c "import sys; print sys.stdout.isatty()" | cat 
True 

(हम देखते हैं यानी पैदा की प्रक्रिया सोच अपने stdout कि में बरगलाया जाता है कि दूसरे आदेश में हम अपने लक्ष्य को हासिल किया है, एक tty है।)

लेकिन समस्या यह है कि यदि हम pty.spawn का उपयोग करते हैं तो इसका इनपुट प्रतिबिंबित नहीं होता है, बल्कि इसे मास्टर के स्टडआउट पर रीडायरेक्ट किया जा रहा है। यह निम्न आदेश के द्वारा देखा जा सकता है:

$ python -c "import sys; import pty; pty.spawn(sys.argv[1:])" cat > out.txt 
$ # Typed "hello" in input, but that is not echoed (use ^D to exit). It is redirected to output.txt 
$ cat out.txt 
hello 
hello 

(लेकिन इस समस्या मौजूद नहीं है जब हम subprocess.call

$ python -c "import sys; from subprocess import call; call(sys.argv[1:])" cat > out1.txt 
hello 
$ cat out1.txt 
hello 

का उपयोग के बाद से अपने stdin और stdout सही ढंग से गुरु से जुड़े होते हैं।)

मुझे कोई रास्ता नहीं मिल सका ताकि प्रोग्राम को पायथन द्वारा बुलाया जा सके, जहां यह stdout को एक tty (pty.spawn के समान) देखता है लेकिन इसका इनपुट सही ढंग से प्रतिबिंबित होता है (subprocess.call के समान)। कोई विचार?

उत्तर

3

आप एक फाइल से जुड़े स्टडआउट के साथ एक टर्मिनल बना रहे हैं ताकि सामान्य इको-बैक टर्मिनलों को स्क्रीन के बजाय फ़ाइल में भेजा जा रहा हो।

इम सुनिश्चित करें कि spawn इस तरह सीधे इस्तेमाल किया जा करने का इरादा है नहीं: pty पुस्तकालय pty.fork() प्रदान करता है एक बच्चे प्रक्रिया बनाने के लिए और stdin/stdout के लिए एक फ़ाइल वर्णनकर्ता देता है। लेकिन इसका उपयोग करने के लिए आपको बहुत अधिक कोड की आवश्यकता होगी।

वर्तमान समस्या आप spawn साथ कर रहे हैं को दूर करने, यहाँ दो आसान विकल्प:

विकल्प 1: तुम सब के बारे में फाइल करने के लिए पैदा की कमान के उत्पादन में भेज रहा है परवाह है, तो आप कर सकते हैं

जो इस तरह दिखेगा चलते हैं तो:

start to stdout only 
hello 
complete to stdout only 
(मैं अजगर के लिए पाइप और here फ़ाइलें एक-लाइनर्स नामित पसंद करते हैं) 210

जो दिखाता है कि इनपुट (मैंने हैलो टाइप किया है) और प्रिंट स्टेटमेंट का परिणाम स्क्रीन पर जा रहा है। Out.txt की सामग्री होगी:

$ cat out.txt 
hello 

यही है, जो आपने टाइप किया है।

विकल्प 2: दूसरी ओर तुम बाहर फ़ाइल चाहते तो पैदा की कमान उत्पादन के आसपास अजगर उत्पादन को रोकने के लिए है, तो आप कुछ में थोड़ा और अधिक जटिल है, जैसे की जरूरत है:

python <(cat << EOF 
import sys 
import pty 
import os 
old_stdout = sys.stdout 
sys.stdout = myfdout = os.fdopen(4,"w") 
print 'start to out file only' 
myfdout.flush() 
pty.spawn(sys.argv[1:]) 
print 'complete to out file only' 
sys.stdout = old_stdout 
EOF 
) bash -c 'cat >&4' 4>out.txt 

जो होगा बाहर फ़ाइल में शामिल होंगे

hello 

लेकिन:

0 केवल टर्मिनल जब चलाने (यानी जो कुछ भी आप टाइप) को यह उत्पादन

पृष्ठभूमि: अजगर pty पुस्तकालय शक्तिशाली है: इसकी एक टर्मिनल अजगर प्रक्रियाओं stdout और stdin से जुड़ी उपकरण बनाने। आईडी कल्पना करें कि इसका अधिकांश उपयोग pty.fork() कॉल का उपयोग करेगा ताकि वास्तविक stdin/stdout प्रभावित न हों।

हालांकि आपके मामले में, आपके खोल पर, आपने पाइथन प्रक्रिया के stdout को एक फ़ाइल में रीडायरेक्ट किया। इसके परिणामस्वरूप पीटीआई को भी फाइल से जुड़ा हुआ था, इसलिए स्टडिन पर स्टडीन को प्रतिबिंबित करने की सामान्य कार्रवाई को रीडायरेक्ट किया जा रहा था। नियमित स्टडआउट (स्क्रीन) अभी भी जगह पर था लेकिन नई पीटीआई द्वारा इसका उपयोग नहीं किया जा रहा था।

विकल्प 1 ऊपर के लिए मुख्य अंतर के लिए, pty.spawn कॉल के अंदर कहीं न कहीं होने की stdout के पुनर्निर्देशन स्थानांतरित करने के लिए जब यह गूंज की कोशिश करता है ताकि प्राइवेट बनाया अभी भी वास्तविक टर्मिनल stdout के लिए एक स्पष्ट संबंध नहीं है (है stdin आप टाइप के रूप में)

विकल्प 2 के लिए अंतर एक मनमाना फ़ाइल वर्णनकर्ता पर एक दूसरे चैनल (यानी फ़ाइल वर्णनकर्ता 4) बना सकते हैं और stdout के स्थान पर इस का उपयोग करने के लिए, एक बार आप अजगर के अंदर हैं और जब आप बनाते हैं आपकी तैयार प्रक्रिया (यानी आपकी स्पॉन्ड प्रक्रिया के stdout को उसी फ़ाइल डिस्क्रिप्टर पर रीडायरेक्ट करें)

इनमें से दोनों अंतर पीटीआई को रोकते हैं कि pty.spawn वास्तविक टर्मिनल से अपना स्टडआउट बदल या डिस्कनेक्ट होने से बनाता है। यह stdin की echo-back ठीक से काम करने की अनुमति देता है।

संकुल कि pty पुस्तकालय का उपयोग करें और आप अधिक नियंत्रण दे रहे हैं, लेकिन आप इनमें से अधिकांश pty.fork() का उपयोग करें (और दिलचस्प बात मैंने पाया नहीं किया अभी तक है कि वास्तव में pty.spawn उपयोग करता है)

संपादित यहाँ एक उदाहरण है मिल जाएगा

import sys 
import pty 
import os 
import select 
import time 
import tty 
import termios 

print 'start' 
try: 
    pid, fd = pty.fork() 
    print 'forked' 
except OSError as e: 
    print e 

if pid == pty.CHILD: 
    cmd = sys.argv[1] 
    args = sys.argv[1:] 
    print cmd, args 
    os.execvp(cmd,args) 
else: 
    tty.setraw(fd, termios.TCSANOW) 
    try: 
     child_file = os.fdopen(fd,'rw') 
     read_list = [sys.stdin, child_file] 
     while read_list: 
      ready = select.select(read_list, [], [], 0.1)[0] 
      if not ready and len(read_list) < 2: 
       break 
      elif not ready: 
       time.sleep(1) 
      else: 
       for file in ready: 
        try: 
         line = file.readline() 
        except IOError as e: 
         print "Ignoring: ", e 
         line = None 
        if not line: 
         read_list.remove(file) 
        else: 
         if file == sys.stdin: 
          os.write(fd,line) 
         else: 
          print "from child:", line 
    except KeyboardInterrupt: 
     pass 

संपादित यह questionpty.fork()

के लिए कुछ अच्छे संबंध हैं: pty.fork() का उपयोग करने का

अद्यतन: कोड में कुछ टिप्पणियां करने चाहिए थे कैसे pty.fork() उदाहरण काम करता है:

interpretor pty.fork() करने के लिए कॉल निष्पादित करता है, processesing दो में विभाजित हो: वहाँ अब दो धागे कि दोनों प्रतीत होती हैं अभी pty.fork() कॉल निष्पादित किया है।

एक धागा वह धागा है जो आप मूल रूप से (माता-पिता) में थे और एक नया धागा (बच्चा) है।

माता-पिता में, pid और fd बच्चे की प्रक्रिया आईडी और एक फाइल decriptor तेह बच्चे की stdin और stdout में connnected की तैयारी में हैं:, माता पिता में जब आप fd आप पढ़ रहे हैं क्या करने के लिए लिखा गया है से पढ़ा बच्चे stdout; जब आप fd पर लिखते हैं तो आप बच्चों के स्टडीन को लिख रहे हैं। तो अब, माता-पिता में हमारे पास अन्य धागे के साथ अपने stdout/stdin पर संचार करने का एक तरीका है।

बच्चे में, pid 0 पर सेट किया गया है और fd सेट नहीं है। अगर हम पैरेंट थ्रेड से बात करना चाहते हैं, तो हम यह जानकर stdin/stdout पर पढ़ और लिख सकते हैं कि माता-पिता इसके साथ कुछ कर सकते हैं और करना चाहिए।

दो धागे इस बिंदु से एक ही कोड निष्पादित करने जा रहे हैं, लेकिन हम बता सकते हैं कि हम pid में मान के आधार पर माता-पिता या बच्चे धागे में हैं या नहीं। अगर हम बच्चे और अभिभावक धागे में अलग-अलग चीजें करना चाहते हैं तो हमें केवल एक सशर्त बयान की आवश्यकता है जो बच्चे को एक कोड पथ और माता-पिता को एक अलग कोड पथ से नीचे भेजता है। Thats क्या इस लाइन करता है:

if pid == pty.CHILD: 
    #child thread will execute this code 
    .... 
else 
    #parent thread will execute this code 
    ... 

बच्चे में, हम बस एक नया Pty में नए आदेश अंडे देने के लिए चाहते हैं। os.execvp प्रयोग किया जाता है क्योंकि हम इस विधि के साथ, लेकिन अनिवार्य रूप से इसकी/stdout ही pty.spawn()'. This means the child stdin/stdout are now connected to the command you wanted via a pty. IMmportantly, any input or output from the command (or the pty for that matter) will be available to the parent thread by reading from रूप fd . And the parent can write to the command via pty by writing to fd`

तो अब, माता पिता में, हम वास्तविक stdin कनेक्ट करने के लिए जरूरत है एक टर्मिनल के रूप में Pty पर अधिक नियंत्रण होगा fd पर पढ़ने और लिखने के माध्यम से बच्चे stdin/stdout के लिए। यह है कि अब मूल कोड क्या करता है (else भाग)। असली स्टडीन पर चालू होने वाला कोई भी डेटा fd पर लिखा गया है। fd (अभिभावक द्वारा) से पढ़े गए किसी भी डेटा को stdout पर लिखा गया है। तो माता-पिता का धागा अब एकमात्र चीज है जो असली स्टडीन/स्टडआउट और एफडी के बीच प्रॉक्सी कर रहा है। यदि आप प्रोग्रामिंग के कमांड के इनपुट और आउटपुट के साथ कुछ करना चाहते हैं, तो यह वह जगह है जहां आप इसे करेंगे।

tty.setraw(fd, termios.TCSANOW) 

यह एक तरीका है गूंज वापस कर रोकने के लिए बच्चे में Pty बताने के लिए है:

केवल दूसरी बात यह है कि माता-पिता में क्या होता है इस कॉल है।

यह समस्या आप मूल रूप से कर रहे थे हल करती है: - अपने स्थानीय टर्मिनल केवल माता-पिता धागा से जुड़ा है - सामान्य गूंज वापस जगह में है (यानी पहले अपने इनपुट प्रक्रिया में पारित हो जाता) - की stdout प्रक्रिया पुनः निर्देशित किया जा सकता है - बच्चे की प्रक्रिया अपने stdin

कि तरह लगता है की स्थानीय गूंज वापस नहीं कर करने के लिए कहा गया है - जो आप अपने टर्मिनल stdout के साथ क्या बच्चे प्रक्रिया की stdin/stdout पर कोई प्रभाव नहीं पड़ता बहुत स्पष्टीकरण - अगर किसी के पास स्पष्टता के लिए कोई संपादन है?

+0

समाधान विकल्प 1 और विकल्प 2 बाहरी पायथन प्रक्रिया (यानी जो अन्य प्रोग्राम कहता है/स्पॉन करता है) के लिए मेरे लिए काम नहीं करता है, 'stdout' को फ़ाइल/पाइप पर रीडायरेक्ट किया जाता है, और यह मेरे नियंत्रण में नहीं है (क्योंकि इसे किसी और द्वारा इस तरह से बुलाया जाता है; मेरे पास केवल मेरे नियंत्रण में पाइथन लिपि चलती है)। इसलिए मैं आंतरिक प्रक्रिया में रीडायरेक्ट आउटपुट को चुन और स्थानांतरित नहीं कर सकता। (लेकिन हाँ अगर मेरे पास वह स्वतंत्रता थी तो विकल्प 1 और 2 स्वीकार्य होगा) –

+0

मैंने अंतिम उदाहरण कोड (** EDIT ** में) चलाया और ऐसा लगता है कि वास्तव में मेरी समस्या का समाधान होता है। लेकिन मैं अभी भी अपने सिर को लपेटने के लिए संघर्ष कर रहा हूं कि यह वास्तव में कैसे काम करता है :( –

+0

@ अभिषेक-केडिया Ive ने एक व्याख्या जोड़ा – spacepickle

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