2016-04-21 14 views
0

私は1つのshファイルを持っています。ターゲットLinuxボックスにインストールする必要があります。だから私は、ユーザからの多くの入力を必要とするshファイルのための自動インストールを書く過程にある。例、私が最初に作ったのは./file.shで、それは大きいパラガラフを表示し、ユーザにEnterを押してもらいます。私はこの場所にこだわっている。サブプロセスにキーデータを送信する方法。ここで私が試したことがあります。サブプロセスlinuxのプロセス送信キーとの通信

import subprocess 

def runProcess(exe): 
    global p 
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 
    while(True): 
     retcode = p.poll() #returns None while subprocess is running 
     line = p.stdout.readline() 
     yield line 
     if(retcode is not None): 
     break 

for line in runProcess('./file.sh'.split()): 
    if '[Enter]' in line: 
     print line + 'got it' 
     p.communicate('\r') 

私の理解が間違っている場合は、訂正してください。重複している場合はご容赦ください。あなたは改行と他には何の束を送信する必要がある場合

答えて

2

、以下を行う必要があります。

  1. Popenためstdinがデッドロック
を発生させずに改行を送るパイプ
  • であることを確認してください

    あなたの現在のコードはどちらもしません。働くかもしれない何か(彼らがttyで直接対話を必要とするAPIを使用して、だけではなくstdinを読んでいないと仮定した場合):

    import subprocess 
    import threading 
    
    def feednewlines(f): 
        try: 
         # Write as many newlines as it will take 
         while True: 
          f.write(b'\n') # Write newline, not carriage return 
          f.flush()  # Flush to ensure it's sent as quickly as possible 
        except OSError: 
         return # Done when pipe closed/process exited 
    
    def runProcess(exe): 
        global p 
        # Get stdin as pipe too 
        p = subprocess.Popen(exe, stdin=subprocess.PIPE, 
             stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 
        # Use thread to just feed as many newlines as needed to stdin of subprocess 
        feeder = threading.Thread(target=feednewlines, args=(p.stdin,)) 
        feeder.daemon = True 
        feeder.start() 
    
        # No need to poll, just read until it closes stdout or exits 
        for line in p.stdout: 
         yield line 
        p.stdin.close() # Stop feeding (causes thread to error and exit) 
        p.wait()   # Cleanup process 
    
    # Iterate output, and echo when [Enter] seen 
    for line in runProcess('./file.sh'.split()): 
        if '[Enter]' in line: 
         print line + 'got it' 
    

    あなたが応答をカスタマイズする必要がある場合には、あなたはするつもりです親スレッドとフィーダスレッドの間の通信を追加する必要があります。これにより、端末に接続されていなくても、子プロセスがプロンプトを出すときに子プロセスが出力を正しくフラッシュしている場合にのみ機能します。

    import queue # Queue on Python 2 
    
    feederqueue = queue.Queue() 
    

    が、その後にフィーダー機能を変更します:

    def feednewlines(f): 
        try: 
         while True: 
          f.write(feederqueue.get()) 
          f.flush() 
        except OSError: 
         return 
    

    とまでグローバルコード下に変更:

    for line in runProcess('./file.sh'.split()): 
        if '[Enter]' in line: 
         print line + 'got it' 
         feederqueue.put(b'\n') 
        elif 'THING THAT REQUIRES YOU TO TYPE FOO' in line: 
         feederqueue.put(b'foo\n') 
    

    などあなたは、グローバルキューを定義するには、このような何かを行う可能性があります

  • +0

    出力から[Enter]を見ると、私はフィードラインと呼ばれていました。しかし、まだそれは動作していません。それはプロセスにEnterを与えていません。私が入力を与えると、次の一連の質問がユーザーの入力に対して表示されます。間違いはどこにあるの? –

    +1

    @Pasupathi: '[Enter]'が表示されているときに 'feednewlines'を呼び出さないで、積極的にフィードするスレッドを起動します。 'runProcess'の外側からもう一度呼び出さないでください。そうしないと、2つのスレッドがサブプロセスの出力を消費するスレッドがなくても、より多くの改行を供給しようとするとデッドロックになります。投稿したコードを実行した場合、スレッドは単独で起動して作業を行います。あなたが望むように動作しない場合は、使用しているプログラムが、キーの押下をキャプチャするために非'stdin'メソッドを使用している可能性があります。 'stdin'を与えることはできません。 – ShadowRanger

    +0

    それは働いた!私はそれを呼ぶべきではありません。今度は次の質問と回答のためにそれを拡張します –

    1

    コマンドラインプログラムは、ターミナルで実行されているときと、実行されているときn背景。プログラムが端末に接続されている場合、プログラムは対話型の回線モードで実行され、ユーザーの操作が必要です。 stdinがファイルまたはパイプである場合、stdinはブロックモードで実行されます。ブロックモードでは、特定のブロックサイズがバッファされるまで書き込みが遅延します。パイプを使用し、データがサブプロセスの出力バッファに残っているため、プログラムは[Enter]プロンプトを表示しません。

    python pexpectモジュールは、端末をエミュレートして一連の "expect"ステートメントでプログラムとやりとりできるようにすることで、この問題を解決します。

    我々はテストプログラム

    #!/usr/bin/env python3 
    data = input('[Enter]') 
    print(data) 
    

    その退屈を実行するとします。データの入力を促し、印刷してから終了します。 pexpect

    #!/usr/bin/env python3 
    
    import pexpect 
    
    # run the program 
    p = pexpect.spawn('./test.py') 
    # we don't need to see our input to the program echoed back 
    p.setecho(False) 
    # read lines until the desired program output is seen 
    p.expect(r'\[Enter\]') 
    # send some data to the program 
    p.sendline('inner data') 
    # wait for it to exit 
    p.expect(pexpect.EOF) 
    # show everything since the previous expect 
    print(p.before) 
    
    print('outer done') 
    
    +0

    ここで愚かな質問をすることはできますか?私は完全にPythonを初めて使っています。 –

    +2

    @Pasupathi:サードパーティのパッケージで、PyPIからダウンロードします。私の解決策が適用されない場合は、 'pexpect'が正しいです。これはPython 2のバージョンを使用していますか?溶液。 – ShadowRanger