调用外部命令/可执行文件

Python中可以执行shell命令的相关模块和函数有:

os.system
os.spawn*
os.popen*          --废弃
popen2.*           --废弃
commands.*      --废弃,3.x中被移除

随着Python版本的更新,过多的模块引起代码的复杂与冗余,因此Python新引入了一个模块subprocess,将以上几个模块中的功能集中到它当中,以后我们只需import这一个即可。

1、subprocess模块

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

  • 运行python的时候,我们都是在创建并运行一个进程。像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序。在Python中,我们通过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序。
  • subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用。另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。

1.1 call

父进程等待子进程执行命令,返回子进程执行命令的状态码(returncode,相当于Linux exit code),如果出现错误,不进行报错。

  • 这里说的返回执行命令的状态码的意思是:如果我们通过一个变量 res = subprocess.call(['dir',shell=True]) 获取的执行结果,我们能获取到的是子进程执行命令执行结果的状态码,即res=0/1 执行成功或者不成功,并不代表说看不到执行结果,在Python的console界面中我们是能够看到命令结果的,只是获取不到。想获取执行的返回结果,请看check_output。
  • 不进行报错解释:如果我们执行的命令在执行时,操作系统不识别,系统会返回一个错误,如:abc命令不存在,这个结果会在console界面中显示出来,但是我们的Python解释器不会提示任何信息,如果想让Python解释器也进行报错,请看check_call
    示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import subprocess
print "1. ################## subprocess.call ###############"
print u"call方法调用系统命令进行执行,如果出错不报错"
subprocess.call(['dir'],shell=True)

:shell默认为False,在Linux下,shell=False时, Popen调用os.execvp()执行args指定的程序;shell=True时,如果args是字符串,Popen直接调用系统的Shell来执行args指定的程序,如果args是一个序列,则args的第一项是定义程序命令字符串,其它项是调用系统Shell时的附加参数。
  在Windows下,不论shell的值如何,Popen调用CreateProcess()执行args指定的外部程序。如果args是一个序列,则先用list2cmdline()转化为字符串,但需要注意的是,并不是MS Windows下所有的程序都可以用list2cmdline来转化为命令行字符串。在windows下,调用脚本时要写上shell=True。

1.2 check_call

父进程等待子进程执行命令,返回执行命令的状态码,如果出现错误,进行报错。如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查。
示例

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import subprocess

print "2. ################## subprocess.check_call ##########"
print u"check_call与call命令相同,区别是如果出错会报错"
subprocess.check_call(['dir'],shell=True)
subprocess.check_call(['abc'],shell=True)
print u"call方法与check_call方法都知识执行并打印命令到输出终端,但是 
 获取不到,如果想获取到结果使用check_output"

1.3 check_output

父进程等待子进程执行命令,返回子进程向标准输出发送输出运行结果,检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性和output属性,output属性为标准输出的输出结果,可用try…except…来检查。
示例

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import subprocess

print "3. ################## subprocess.check_output ##############"
res1 = subprocess.call(['dir'],shell=True)
res2 = subprocess.check_call(['dir'],shell=True)
res3 = subprocess.check_output(['dir'],shell=True)
print u"call结果:",res1
print u"check_call结果:",res2
print u"check_output结果:\n",res3

可见,call/check_call 返回值均是命令的执行状态码,而check_output返回值是命令的执行结果。
如果在执行相关命令时,命令后带有参数,将程序名(即命令)和所带的参数一起放在一个列表中传递给相关方法即可,例如:

import subprocess

retcode = subprocess.call(["ls", "-l"])
print retcode

1.4 Popen

实际上,subprocess模块中只定义了一个类: Popen。上面的几个函数都是基于Popen()的封装(wrapper)。从Python2.4开始使用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,子进程的环境变量将从父进程中继承。

与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block)。
示例

#!/usr/bin/env python
import subprocess

child = subprocess.Popen(['ping','-c','4','www.baidu.com'])  #创建一个子进 程,进程名为child,执行操作ping -c 4 www.baidu.com
child.wait()  #子进程等待
print 'hello'

此外也可以在父进程中对子进程进行其它操作。

child.poll() # 用于检查子进程是否已经结束。设置并返回returncode属性。
child.wait() # 等待子进程结束。设置并返回returncode属性。

Popen.communicate(input=None) 
# 与子进程进行交互。向stdin发送数 据,或从stdout和stderr中读取数  
据。可选参数input指定发送到子进程的参数。Communicate()返回一个元 
组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送  
数据,在创建Popen对象的时候,参数 stdin必须被设置为PIPE。同样, 
如果希望从stdout和stderr获取数据,必 须将stdout和stderr设置为PIPE。
    
child.kill() # 终止子进程
child.send_signal() # 向子进程发送信号
child.terminate() # 终止子进程
child.pid # 获取子进程的进程ID。
child.returncode # 获取进程的返回值。如果进程还没有结束,返回None。

子进程文本流控制
子进程的标准输入、标准输出和标准错误如下属性分别表示:
child.stdin | child.stdout | child.stderr
我们还可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe),如下2个例子:
例1

#!/usr/bin/env python
import subprocess

child = subprocess.Popen(['ls','-l'],stdout=subprocess.PIPE)  #将标准输出定向输出到subprocess.PIPE
print child.stdout.read()   #使用 child.communicate()  也可

例2

#!/usr/bin/env python
import subprocess

child1 = subprocess.Popen(['cat','/etc/passwd'],stdout=subprocess.PIPE)
child2 = subprocess.Popen(['grep','root'],stdin=child1.stdout,stdout=subprocess.PIPE)

print child2.communicate()

subprocess.PIPE实际上为文本流提供一个缓存区。child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走。child2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。
注意:communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成

1.5 进程通信

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

  • 简单情况

例1

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

subprocess.call(["ls", "-l"])

subprocess.call("exit 1", shell=True)

shell参数根据你要执行的命令的情况来决定,上面是dir命令,就一定要shell=True了,p.wait()可以得到命令的返回值。
如果上面写成a=p.wait(),a就是returncode。那么输出a的话,有可能就是0【表示执行成功】。

  • 分开输出

例2

p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)  
(stdoutput,erroutput) = p.communicate()  

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

  • 合并输出

例3

p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
(stdoutput,erroutput) = p.communicate()  

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

  • 逐行输出

例4

p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
while True:  
    buff = p.stdout.readline()  
    if buff == '' and p.poll() != None:  
        break  
  • 死锁

如果使用了管道,而又不去处理管道的输出,如果子进程输出数据过多,死锁就会发生了。
例5

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

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

  • 多命令执行

在shell中我们知道,想要连接多个命令可以使用管道。
在subprocess中,可以使用上一个命令执行的输出结果作为下一次执行的输入。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351

推荐阅读更多精彩内容

  • 处于学习别人代码风格阶段,github参考学习程序程序开头会有 一是用来指定脚本语言为 Python,二是用来指定...
    lifesmily阅读 1,045评论 0 0
  • 1.os.system('cmd') os.system('cat /proc/cpuinfo') 直接执行参数中...
    奇_66a0阅读 13,994评论 0 5
  • From: https://blog.linuxeye.com/375.html 从Python 2.4开始,Py...
    pzka158阅读 3,004评论 0 2
  • 其实应该搞清楚何时要用python写脚本,何时用shell例如对于系统操作的业务,我倾向于用shell awk ...
    帅子锅阅读 12,939评论 0 6
  • 以下是丹麦国家旅游局关于“博恩霍姆岛”的官方介绍: ================== 博恩霍姆岛是波罗的海里的...
    MW_WM阅读 1,231评论 0 1