您正在創建一個stdout連接到文件的終端,以便終端所執行的正常回顯被髮送到文件而不是屏幕。
我不確定spawn
是否打算像這樣直接使用:pty
庫提供pty.fork()
來創建子進程並返回stdin/stdout的文件描述符。但是你需要更多的代碼來使用它。
爲了克服你與spawn
具有當前問題,繼承人兩個簡單的選擇:
選項1:如果所有你關心的是發送催生命令的輸出到一個文件,那麼你可以做(我喜歡命名管道和here
文件蟒蛇單行):
它看起來像這樣運行時:
start to stdout only
hello
complete to stdout only
這表明輸入(我鍵入hello)和打印語句的結果將進入屏幕。 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
這將唯一有此輸出運行時(不管你鍵入IE)終端:
hello
但出文件將包含:
$ cat out.txt
start to out file only
hello
complete to out file only
背景: python pty
庫功能強大:它創建一個連接到python進程stdout和stdin的終端設備。編號想象這將使用大部分的使用pty.fork()
調用,以便真正的標準輸入/標準輸出不受影響。
但就你而言,在你的shell中,你將python進程的標準輸出重定向到一個文件。由此產生的pty也因此將其stdout附加到該文件,因此將stdin回顯到stdout的正常操作正在被重定向。常規stdout(屏幕)仍然存在,但未被新的pty使用。
爲選項1以上的主要區別是將標準輸出重定向到某處發生pty.spawn
呼叫內,從而使PTY創建仍然具有實際終端標準輸出清晰的連接(當它試圖回聲標準輸入您鍵入)
爲選項2所不同的是,以創建一個任意的文件描述符的第二信道(即文件描述符4)和在地方標準輸出的利用這一點,一旦你內部蟒並在創建你生成的過程(即將你生成的過程的標準輸出重定向到相同的文件描述符)
這兩種差異都會阻止pty.spawn
創建的pty將其stdout從實際終端更改或斷開連接。這可以讓stdin的回顯正常工作。
有跡象表明,使用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
編輯這question有pty.fork()
一些很好的鏈接:使用pty.fork()的
UPDATE:應該把一些註釋代碼 如何pty.fork()
示例工作:
當解釋器執行調用pty.fork()
的處理加工分成兩個:現在有兩個線程都出現剛剛執行了pty.fork()
調用。
一個線程是您最初在(父)和一個是新線程(子)的線程。
在父,在pid
和fd
設置爲孩子的進程ID和connnected到TEH孩子的標準輸入和標準輸出文件decriptor:父,當你從fd
你正在閱讀的內容已被寫入讀孩子們stdout;當你寫信給fd
時,你正在給孩子寫信。所以現在,在父類中,我們有一種通過stdout/stdin與其他線程進行通信的方式。
在孩子中,pid
設置爲0,而fd
未設置。如果我們想要與父線程交談,我們可以讀寫標準輸入/標準輸出,知道父母可以並且應該對此做些什麼。
這兩個線程將從這一點開始執行相同的代碼,但我們可以根據pid
中的值來判斷我們是處於父代還是子線程中。如果我們想要做的兒童和家長的線程不同的東西,然後我們只需要發送孩子下來一個代碼路徑和家長下了不同的代碼路徑條件語句。那是這條線做什麼:
if pid == pty.CHILD:
#child thread will execute this code
....
else
#parent thread will execute this code
...
在孩子,我們只是想在一個新的PTY產卵新的命令。該os.execvp
用來監守我們將在pty的控制,用這種方法但本質上它一樣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`
所以現在,在父母,我們需要連接的真正標準輸入/輸出端子通過閱讀和寫入到fd
的孩子標準輸入/標準輸出。那就是現在的母代碼(else
部分)。任何出現在真實stdin上的數據都會寫入fd
。從fd
(由父級)讀取的任何數據都寫入stdout。所以父線程現在做的唯一事情就是在真正的stdin/stdout和fd之間進行代理。如果您想以編程方式對您的輸入和輸出進行操作,這就是您要做的地方。
這種情況發生在父母唯一的其他事情是這樣的呼籲:
tty.setraw(fd, termios.TCSANOW)
這是給我們說說這個孩子PTY停止做回聲回的一種方式。
這解決了你最初遇到的問題: - 你的本地終端僅連接到父線程 - 正常回聲回到位(即之前的輸入被傳遞到處理) - 標準輸出的過程可以被重定向 - 無論你與你的終端標準輸出做對孩子的過程 的標準輸入/輸出沒有影響 - 子進程已被告知不要做它的標準輸入
這似乎是當地的回聲回很多解釋 - 如果任何人有任何清晰的編輯?
解決方案option1和option2不適用於我,因爲對於外部python進程(即調用/衍生其他程序的那個進程),'stdout'被重定向到一個文件/管道,並且這不在我的控制之下(因爲它由其他人調用;我只有在我的控制下運行的python腳本)。所以我不能選擇並將輸出重定向到內部進程。 (但是,是的,如果我有這樣的自由,然後選項1&2本來是可以接受的) –
我跑過去的例子代碼(**編輯**),這似乎真正解決我的問題。但我仍然在努力總結我的頭周圍的實際工作:( –
@阿布舍克 - 伊夫KEDIA增加了一個交代 – spacepickle