Python 3 利用 subprocess 实现管道( pipe )交互操作读/写通信
Posted Suwings
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 3 利用 subprocess 实现管道( pipe )交互操作读/写通信相关的知识,希望对你有一定的参考价值。
这里我们用Windows下的shell来举例:
from subprocess import * #因为是举例,就全部导入了
为了方便你理解,我们用一个很简单的一段代码来说明:
可以看见我们利用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 吧,味道也许更佳喔~
1 #run.py 2 3 from subprocess import * 4 import threading 5 import time 6 7 p =Popen(\'cmd.exe\',shell=True,stdin=PIPE,stdout=PIPE) 8 9 def run(): 10 global p 11 while True: 12 line = p.stdout.readline() 13 if not line: #空则跳出 14 break 15 print(">>>>>>",line.decode("GBK")) 16 17 print("look up!!! EXIT ===") #跳出 18 19 20 w =threading.Thread(target=run) 21 22 p.stdin.write("echo HELLW_WORLD!\\r\\n".encode("GBK")) 23 p.stdin.flush() 24 time.sleep(1) #延迟是因为等待一下线程就绪 25 p.stdin.write("exit\\r\\n".encode("GBK")) 26 p.stdin.flush() 27 28 w.start()
很好很好,猜猜输出什么?
有很多换行的原因是cmd返回的结果有换行,然后print输出会加一个换行,所以就换了两行,你可以考虑使用 sys.stdout.write 来输出,这样就没有附加的换行了
这样的话,你可以制作一个基础的读写了,那么我们开始封装吧。
封装Pipe
不废话了,直接上代码,如果你真的想学会的话,还请认真自己读读代码。
110行
我们实现了将所有的过程集中在一个类里面,并且可以定义三个参数,退出反馈函数,就绪反馈函数和输出反馈函数。
1 # -*- coding:utf-8 -*- 2 3 import subprocess 4 import sys 5 import threading 6 7 class LoopException(Exception): 8 """循环异常自定义异常,此异常并不代表循环每一次都是非正常退出的""" 9 def __init__(self,msg="LoopException"): 10 self._msg=msg 11 12 def __str__(self): 13 return self._msg 14 15 16 17 class SwPipe(): 18 """ 19 与任意子进程通信管道类,可以进行管道交互通信 20 """ 21 def __init__(self,commande,func,exitfunc,readyfunc=None, 22 shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,code="GBK"): 23 """ 24 commande 命令 25 func 正确输出反馈函数 26 exitfunc 异常反馈函数 27 readyfunc 当管道创建完毕时调用 28 """ 29 self._thread = threading.Thread(target=self.__run,args=(commande,shell,stdin,stdout,stderr,readyfunc)) 30 self._code = code 31 self._func = func 32 self._exitfunc = exitfunc 33 self._flag = False 34 self._CRFL = "\\r\\n" 35 36 def __run(self,commande,shell,stdin,stdout,stderr,readyfunc): 37 """ 私有函数 """ 38 try: 39 self._process = subprocess.Popen( 40 commande, 41 shell=shell, 42 stdin=stdin, 43 stdout=stdout, 44 stderr=stderr 45 ) 46 except OSError as e: 47 self._exitfunc(e) 48 fun = self._process.stdout.readline 49 self._flag = True 50 if readyfunc != None: 51 threading.Thread(target=readyfunc).start() #准备就绪 52 while True: 53 line = fun() 54 if not line: 55 break 56 try: 57 tmp = line.decode(self._code) 58 except UnicodeDecodeError: 59 tmp = \\ 60 self._CRFL + "[PIPE_CODE_ERROR] <Code ERROR: UnicodeDecodeError>\\n" 61 + "[PIPE_CODE_ERROR] Now code is: " + self._code + self._CRFL 62 self._func(self,tmp) 63 64 self._flag = False 65 self._exitfunc(LoopException("While Loop break")) #正常退出 66 67 68 def write(self,msg): 69 if self._flag: 70 #请注意一下这里的换行 71 self._process.stdin.write((msg + self._CRFL).encode(self._code)) 72 self._process.stdin.flush() 73 #sys.stdin.write(msg)#怎么说呢,无法直接用代码发送指令,只能默认的stdin 74 else: 75 raise LoopException("Shell pipe error from \'_flag\' not True!") #还未准备好就退出 76 77 78 def start(self): 79 """ 开始线程 """ 80 self._thread.start() 81 82 def destroy(self): 83 """ 停止并销毁自身 """ 84 process.stdout.close() 85 self._thread.stop() 86 del self 87 88 89 90 91 92 93 if __name__ == \'__main__\': #那么我们来开始使用它吧 94 e = None 95 96 #反馈函数 97 def event(cls,line):#输出反馈函数 98 sys.stdout.write(line) 99 100 def exit(msg):#退出反馈函数 101 print(msg) 102 103 def ready():#线程就绪反馈函数 104 e.write("dir") #执行 105 e.write("ping www.baidu.com") 106 e.write("echo Hello!World 你好中国!你好世界!") 107 e.write("exit") 108 109 e = SwPipe("cmd.exe",event,exit,ready) 110 e.start()
输出:
你可以看见,我们的指令都顺序的执行了。当然了这里面还有OS的功劳。
那么你的可扩展的Pipe类应该已经构建完毕了吧?
A: 我之所以要在这种代码前面加入行数的原因就是为了防止你复制;因为你可能永远不会明白这里究竟发生了什么,而是只懂得了使用。
顺便一提:
最好去参考一下官方的文档,已经讲得非常详细了。subprocess.Popen.communicate 或许更适合你,看你是要进行什么事情。
参考:
https://docs.python.org/3/library/subprocess.html
到此结束,如有错误之处还望指正。
不论是否对你有帮助,感谢你耐心阅读
以上是关于Python 3 利用 subprocess 实现管道( pipe )交互操作读/写通信的主要内容,如果未能解决你的问题,请参考以下文章
python 利用subprocess调用cmd命令程序,并正确输出控制台的输出中文