欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

详解Python3利subprocess实现管道pipe交互操作读/写通信方法

程序员文章站 2022-05-08 17:54:39
...
这里我们用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 '_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()

输出:

详解Python3利subprocess实现管道pipe交互操作读/写通信方法

你可以看见,我们的指令都顺序的执行了。当然了这里面还有OS的功劳。

那么你的可扩展的Pipe类应该已经构建完毕了吧?

A: 我之所以要在这种代码前面加入行数的原因就是为了防止你复制;因为你可能永远不会明白这里究竟发生了什么,而是只懂得了使用。

顺便一提:

最好去参考一下官方的文档,已经讲得非常详细了。subprocess.Popen.communicate 或许更适合你,看你是要进行什么事情。

以上就是详解Python3利subprocess实现管道pipe交互操作读/写通信方法的详细内容,更多请关注其它相关文章!