首頁  >  文章  >  後端開發  >  詳解Python3利subprocess實作管道pipe互動操作讀/寫通訊方法

詳解Python3利subprocess實作管道pipe互動操作讀/寫通訊方法

高洛峰
高洛峰原創
2017-03-20 10:28:124170瀏覽

這裡我們用Windows下的shell來舉例:

 subprocess  *

為了方便你理解,我們用一個很簡單的一段程式碼來說明:

詳解Python3利subprocess實作管道pipe互動操作讀/寫通訊方法

# #可以看見我們利用Popen實例化了一個p,創建了子程式cmd.exe,然後我們給他的的Stdin(標準輸入流)Stdout(標準輸出流);

同時使用了subprocess. PIPE 作為參數,這個是一個特殊值,用來表示這些通道要開放。 (在

Python3.5,加入了run()方法來進行更好的操作)

#然後我們繼續

詳解Python3利subprocess實作管道pipe互動操作讀/寫通訊方法##這些訊息是不是很眼熟?這都是cmd的標準輸出!

然後就會輸出這些:

詳解Python3利subprocess實作管道pipe互動操作讀/寫通訊方法我們剛剛所寫入的資訊"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()

很好很好,猜猜輸出什麼?

詳解Python3利subprocess實作管道pipe互動操作讀/寫通訊方法有很多換行的原因是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 &#39;_flag&#39; not True!")  #还未准备好就退出


    def start(self):
        """ 开始线程 """
        self._thread.start()

    def destroy(self):
        """ 停止并销毁自身 """
        process.stdout.close()
        self._thread.stop()
        del self
       





if __name__ == &#39;__main__&#39;:   #那么我们来开始使用它吧
    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()
輸出:

 

詳解Python3利subprocess實作管道pipe互動操作讀/寫通訊方法 你可以看見,我們的指令都順序的執行了。當然了這裡面還有OS的功勞。

 

那麼你的可擴充的Pipe類別應該已經建構完畢了吧?

A: 我之所以要在這種程式碼前面加入行數的原因就是為了防止你複製;因為你可能永遠不會明白這裡究竟發生了什麼,而是只懂得了使用。

順便一提:

最好去參考一下官方的文檔,已經講得非常詳細了。 subprocess.Popen.communicate 或許更適合你,看看你要做什麼事。

 

以上是詳解Python3利subprocess實作管道pipe互動操作讀/寫通訊方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn