博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python中subprocess里call与popen的用法区别
阅读量:6711 次
发布时间:2019-06-25

本文共 4503 字,大约阅读时间需要 15 分钟。

subprocess的目的就是启动一个新的进程并且与之通信。

subprocess模块中只定义了一个类: Popen。可以使用Popen来创建进程,并与进程进行复杂的交互。它的构造函数如下:

subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

参数args可以是字符串或者序列类型(如:list,元组),用于指定进程的可执行文件及其参数。如果是序列类型,第一个元素通常是可执行文件的路径。我们也可以显式的使用executeable参数来指定可执行文件的路径。

参数stdin, stdout, stderr分别表示程序的标准输入、输出、错误句柄。他们可以是PIPE,文件描述符或文件对象,也可以设置为None,表示从父进程继承。

如果参数shell设为true,程序将通过shell来执行。

参数env是字典类型,用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。

subprocess.PIPE

  在创建Popen对象时,subprocess.PIPE可以初始化stdin, stdout或stderr参数。表示与子进程通信的标准流。

subprocess.STDOUT

  创建Popen对象时,用于初始化stderr参数,表示将错误通过标准输出流输出。

Popen的方法:

Popen.poll()

  用于检查子进程是否已经结束。设置并返回returncode属性。

Popen.wait()

  等待子进程结束。设置并返回returncode属性。

Popen.communicate(input=None)

  与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。Communicate()返回一个元组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。

Popen.send_signal(signal)

  向子进程发送信号。

Popen.terminate()

  停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。

Popen.kill()

  杀死子进程。

Popen.stdin,Popen.stdout ,Popen.stderr ,官方文档上这么说:

stdinstdout and stderr specify the executed programs’ standard input, standard output and standard error file handles, respectively. Valid values are , an existing file descriptor (a positive integer), an existing file object, and None.

Popen.pid

  获取子进程的进程ID。

Popen.returncode

  获取进程的返回值。如果进程还没有结束,返回None。

---------------------------------------------------------------

简单的用法:

 
  1. p=subprocess.Popen("dir", shell=True)  
  2. p.wait()  

shell参数根据你要执行的命令的情况来决定,上面是dir命令,就一定要shell=True了,p.wait()可以得到命令的返回值。

如果上面写成a=p.wait(),a就是returncode。那么输出a的话,有可能就是0【表示执行成功】。

---------------------------------------------------------------------------

进程通讯

如果想得到进程的输出,管道是个很方便的方法,这样:

 
  1. p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)  
  2. (stdoutput,erroutput) = p.<span>commu</span>nicate()  
 

p.communicate会一直等到进程退出,并将标准输出和标准错误输出返回,这样就可以得到子进程的输出了。

再看一个communicate的例子。

上面的例子通过communicate给stdin发送数据,然后使用一个tuple接收命令的执行结果。

------------------------------------------------------------------------

上面,标准输出和标准错误输出是分开的,也可以合并起来,只需要将stderr参数设置为subprocess.STDOUT就可以了,这样子:

  1. p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
  2. (stdoutput,erroutput) = p.<span>commu</span>nicate()  

如果你想一行行处理子进程的输出,也没有问题:

 
  1. p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
  2. while True:  
  3.     buff = p.stdout.readline()  
  4.     if buff == '' and p.poll() != None:  
  5.         break  

------------------------------------------------------

死锁

但是如果你使用了管道,而又不去处理管道的输出,那么小心点,如果子进程输出数据过多,死锁就会发生了,比如下面的用法:

 
  1. p=subprocess.Popen("longprint", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
  2. p.wait()  

longprint是一个假想的有大量输出的进程,那么在我的xp, Python2.5的环境下,当输出达到4096时,死锁就发生了。当然,如果我们用p.stdout.readline或者p.communicate去清理输出,那么无论输出多少,死锁都是不会发生的。或者我们不使用管道,比如不做重定向,或者重定向到文件,也都是可以避免死锁的。

----------------------------------

subprocess还可以连接起来多个命令来执行。

在shell中我们知道,想要连接多个命令可以使用管道。

在subprocess中,可以使用上一个命令执行的输出结果作为下一次执行的输入。例子如下:

例子中,p2使用了第一次执行命令的结果p1的stdout作为输入数据,然后执行tail命令。

- -------------------

下面是一个更大的例子。用来ping一系列的ip地址,并输出是否这些地址的主机是alive的。代码参考了 unix 系统管理指南。

 
  1. #!/usr/bin/env python  
  2.   
  3. from threading import Thread  
  4. import subprocess  
  5. from Queue import Queue  
  6.   
  7. num_threads=3  
  8. ips=['127.0.0.1','116.56.148.187']  
  9. q=Queue()  
  10. def pingme(i,queue):  
  11.     while True:  
  12.         ip=queue.get()  
  13.         print 'Thread %s pinging %s' %(i,ip)  
  14.         ret=subprocess.call('ping -c 1 %s' % ip,shell=True,stdout=open('/dev/null','w'),stderr=subprocess.STDOUT)  
  15.         if ret==0:  
  16.             print '%s is alive!' %ip  
  17.         elif ret==1:  
  18.             print '%s is down...'%ip  
  19.         queue.task_done()  
  20.   
  21. #start num_threads threads  
  22. for i in range(num_threads):  
  23.     t=Thread(target=pingme,args=(i,q))  
  24.     t.setDaemon(True)  
  25.     t.start()  
  26.   
  27. for ip in ips:  
  28.     q.put(ip)  
  29. print 'main thread waiting...'  
  30. q.join();print 'Done'  
 

在上面代码中使用subprocess的主要好处是,使用多个线程来执行ping命令会节省大量时间。

假设说我们用一个线程来处理,那么每个 ping都要等待前一个结束之后再ping其他地址。那么如果有100个地址,一共需要的时间=100*平均时间。

如果使用多个线程,那么最长执行时间的线程就是整个程序运行的总时间。【时间比单个线程节省多了】

这里要注意一下Queue模块的学习。

pingme函数的执行是这样的:

启动的线程会去执行pingme函数。

pingme函数会检测队列中是否有元素。如果有的话,则取出并执行ping命令。

这个队列是多个线程共享的。所以这里我们不使用列表。【假设在这里我们使用列表,那么需要我们自己来进行同步控制。Queue本身已经通过信号量做了同步控制,节省了我们自己做同步控制的工作=。=】

代码中q的join函数是阻塞当前线程。下面是e文注释

 Queue.join()

  Blocks until all items in the queue have been gotten and processed(task_done()).

转载于:https://www.cnblogs.com/Hessen/p/8944612.html

你可能感兴趣的文章
【转载】已知经纬度查询Landsat TM 、ETM+ 、MSS 数据的行带 整理版 V1.0
查看>>
AE开发的一个想法
查看>>
vsftpd
查看>>
自签名证书和私有CA签名的证书的区别 创建自签名证书 创建私有CA 证书类型 证书扩展名(转)...
查看>>
吴恩达课后作业学习2-week1-1 初始化
查看>>
Phone List
查看>>
css3系列之animation
查看>>
rsync 参数断点续传
查看>>
padding和margin的区别,以及其存在的bug和消除方法!
查看>>
Nautilus-Share-Message: Called "net usershare info" but it failed: Failed to
查看>>
统计某个单词出现次数
查看>>
error SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443
查看>>
南屿 带你 走进 vue
查看>>
iOS边练边学--级联菜单的两种实现方法
查看>>
使用SimpleXML 解析xml
查看>>
第三周作业
查看>>
第四章6
查看>>
2018/12/01 一个64位操作系统的实现 第四章 导入kernel.bin(3)
查看>>
split的用法回顾,快忘记了@ →@
查看>>
正则表达式的简单应用
查看>>