這裡我們用Windows下的shell來舉例:
subprocess *
為了方便你理解,我們用一個很簡單的一段程式碼來說明:
# #可以看見我們利用Popen實例化了一個p,創建了子程式cmd.exe,然後我們給他的的Stdin(標準輸入流)Stdout(標準輸出流);
同時使用了subprocess. PIPE 作為參數,這個是一個特殊值,用來表示這些通道要開放。 (在
Python3.5,加入了run()方法來進行更好的操作)
#然後我們繼續
##這些訊息是不是很眼熟?這都是cmd的標準輸出!
然後就會輸出這些:
我們剛剛所寫入的資訊"echo Hellwworlds\r\n"已經被寫入了,看起來確實成功了!
注意一下我們使用了p.stdin.
flush
() 來對輸入快取區進行刷新,輸出的資訊也需要一個"\r\n",至少在Windows 系統下必須這樣做,否則只刷新(p.stdin.flush)的話是無效的;我們到底做了什麼?
我們成功的創建了子程式cmd.exe,並且寫入"echo Hellwworlds\r\n" ,然後cmd取得了並且執行,於是返回Hellwworlds,這就是一次很簡單的讀寫交互!
更高級的使用
既然我們已經可以簡單的讀寫了,那麼加點for和threading 吧,味道也許更佳喔~
#run.py
from subprocess import *
import threading
import time
p =Popen('cmd.exe',shell=True,stdin=PIPE,stdout=PIPE)
def run():
global p
while True:
line = p.stdout.readline()
if not line: #空则跳出
break
print(">>>>>>",line.decode("GBK"))
print("look up!!! EXIT ===") #跳出
w =threading.Thread(target=run)
p.stdin.write("echo HELLW_WORLD!\r\n".encode("GBK"))
p.stdin.flush()
time.sleep(1) #延迟是因为等待一下线程就绪
p.stdin.write("exit\r\n".encode("GBK"))
p.stdin.flush()
w.start()
很好很好,猜猜輸出什麼?
有很多換行的原因是cmd回傳的結果有換行,然後print輸出會加上一個換行,所以就換了兩行,你可以考慮使用 sys.stdout .write 來輸出,這樣就沒有附加的換行了
這樣的話,你可以製作一個基礎的讀寫了,那麼我們開始封裝吧。
封裝Pipe
# 不廢話了,直接上程式碼,如果你真的想學會的話,請認真自己讀讀代碼。
110行
我們實作了將所有的流程集中在一個類別裡面,並且可以定義三個參數,退出回饋
函數
,就緒回饋函數和輸出回饋函數。 # -*- coding:utf-8 -*-
import subprocess
import sys
import threading
class LoopException(Exception):
"""循环异常自定义异常,此异常并不代表循环每一次都是非正常退出的"""
def __init__(self,msg="LoopException"):
self._msg=msg
def __str__(self):
return self._msg
class SwPipe():
"""
与任意子进程通信管道类,可以进行管道交互通信
"""
def __init__(self,commande,func,exitfunc,readyfunc=None,
shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,code="GBK"):
"""
commande 命令
func 正确输出反馈函数
exitfunc 异常反馈函数
readyfunc 当管道创建完毕时调用
"""
self._thread = threading.Thread(target=self.__run,args=(commande,shell,stdin,stdout,stderr,readyfunc))
self._code = code
self._func = func
self._exitfunc = exitfunc
self._flag = False
self._CRFL = "\r\n"
def __run(self,commande,shell,stdin,stdout,stderr,readyfunc):
""" 私有函数 """
try:
self._process = subprocess.Popen(
commande,
shell=shell,
stdin=stdin,
stdout=stdout,
stderr=stderr
)
except OSError as e:
self._exitfunc(e)
fun = self._process.stdout.readline
self._flag = True
if readyfunc != None:
threading.Thread(target=readyfunc).start() #准备就绪
while True:
line = fun()
if not line:
break
try:
tmp = line.decode(self._code)
except UnicodeDecodeError:
tmp = \
self._CRFL + "[PIPE_CODE_ERROR] <Code ERROR: UnicodeDecodeError>\n"
+ "[PIPE_CODE_ERROR] Now code is: " + self._code + self._CRFL
self._func(self,tmp)
self._flag = False
self._exitfunc(LoopException("While Loop break")) #正常退出
def write(self,msg):
if self._flag:
#请注意一下这里的换行
self._process.stdin.write((msg + self._CRFL).encode(self._code))
self._process.stdin.flush()
#sys.stdin.write(msg)#怎么说呢,无法直接用代码发送指令,只能默认的stdin
else:
raise LoopException("Shell pipe error from '_flag' not True!") #还未准备好就退出
def start(self):
""" 开始线程 """
self._thread.start()
def destroy(self):
""" 停止并销毁自身 """
process.stdout.close()
self._thread.stop()
del self
if __name__ == '__main__': #那么我们来开始使用它吧
e = None
#反馈函数
def event(cls,line):#输出反馈函数
sys.stdout.write(line)
def exit(msg):#退出反馈函数
print(msg)
def ready():#线程就绪反馈函数
e.write("dir") #执行
e.write("ping www.baidu.com")
e.write("echo Hello!World 你好中国!你好世界!")
e.write("exit")
e = SwPipe("cmd.exe",event,exit,ready)
e.start()
輸出:
你可以看見,我們的指令都順序的執行了。當然了這裡面還有OS的功勞。
那麼你的可擴充的Pipe類別應該已經建構完畢了吧?
A: 我之所以要在這種程式碼前面加入行數的原因就是為了防止你複製;因為你可能永遠不會明白這裡究竟發生了什麼,而是只懂得了使用。
順便一提:
最好去參考一下官方的文檔,已經講得非常詳細了。 subprocess.Popen.communicate 或許更適合你,看看你要做什麼事。
以上是詳解Python3利subprocess實作管道pipe互動操作讀/寫通訊方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!