2017-01-09 14 views
6

インタラクティブなBashインスタンスをPythonの独立したプロセスで実行する必要があります。専用のTTYを使用しています(私はpexpectを使用できません)。 私は一般的に同様のプログラムで使用される参照このコードスニペットを使用:popenと専用のTTY Pythonを使ってインタラクティブなBashを実行する

master, slave = pty.openpty() 

p = subprocess.Popen(["/bin/bash", "-i"], stdin=slave, stdout=slave, stderr=slave) 

os.close(slave) 

x = os.read(master, 1026) 

print x 

subprocess.Popen.kill(p) 
os.close(master) 

しかし、私はそれを実行したときに、私は次の出力を得る:実行の

$ ./pty_try.py 
bash: cannot set terminal process group (10790): Inappropriate ioctl for device 
bash: no job control in this shell 

straceのは、いくつかのエラーを示しています

... 
readlink("/usr/bin/python2.7", 0x7ffc8db02510, 4096) = -1 EINVAL (Invalid argument) 
... 
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7ffc8db03590) = -1 ENOTTY (Inappropriate ioctl for device) 
... 
readlink("./pty_try.py", 0x7ffc8db00610, 4096) = -1 EINVAL (Invalid argument) 

コードスニペットはかなり単純なようですが、Bashは必要なものを手に入れていませんか?何が問題なの?

+2

これは正常です。**対話シェル**には、ジョブコントロールなし**があります。 –

+1

ジョブ制御もしたい場合は、シェルをプロセスリーダーにする必要があります。つまり、新しい "セッション"を開始します。これは、 'Popen'(' Python 3.2以降)の 'start_new_session = True'キーワード引数で実現します。より多くの制御が必要な場合は、 'preexec_fn = ...'を使用してください。 –

+0

それは合理的です。私は 'start_new_session = True'は> 3.2に関連していることを理解しています。 2.7に相当するものはありますか?申し訳ありませんが、おそらく質問のpythonバージョンを言及しているはずです。 – TKKS

答えて

2

これは(qarmaにより示唆されるように)最後に私のために働いていたソリューションです:

libc = ctypes.CDLL('libc.so.6') 

master, slave = pty.openpty() 
p = subprocess.Popen(["/bin/bash", "-i"], preexec_fn=libc.setsid, stdin=slave, stdout=slave, stderr=slave) 
os.close(slave) 

... do stuff here ... 

x = os.read(master, 1026) 
print x 
+1

私はあなたが '-i'をもっと必要とするかどうか疑問に思っています。 –

-3

入力を読み込んでからxを出力する必要があります。

+0

あなたの答えは1つの文で構成されています。私は解読できません。あなたは誰もが感謝すると思いますか? :) –

4

これは、サブプロセスでインタラクティブなコマンドを実行するためのソリューションです。 stdoutをnon-blockingにするために擬似端末を使います(また、いくつかのコマンドにはttyデバイスが必要です、例えばbashなど)。それはselectを使ってサブプロセスへの入力と出力を処理します。

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 

import os 
import sys 
import select 
import termios 
import tty 
import pty 
from subprocess import Popen 

command = 'bash' 
# command = 'docker run -it --rm centos /bin/bash'.split() 

# save original tty setting then set it to raw mode 
old_tty = termios.tcgetattr(sys.stdin) 
tty.setraw(sys.stdin.fileno()) 

# open pseudo-terminal to interact with subprocess 
master_fd, slave_fd = pty.openpty() 

# use os.setsid() make it run in a new process group, or bash job control will not be enabled 
p = Popen(command, 
      preexec_fn=os.setsid, 
      stdin=slave_fd, 
      stdout=slave_fd, 
      stderr=slave_fd, 
      universal_newlines=True) 

while p.poll() is None: 
    r, w, e = select.select([sys.stdin, master_fd], [], []) 
    if sys.stdin in r: 
     d = os.read(sys.stdin.fileno(), 10240) 
     os.write(master_fd, d) 
    elif master_fd in r: 
     o = os.read(master_fd, 10240) 
     if o: 
      os.write(sys.stdout.fileno(), o) 

# restore tty settings back 
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) 
関連する問題