我们经常需要通过Python去执行一条系统命令或脚本,系统的shell命令是独立于你的python进程之外的,每执行一条命令,就是发起一个新进程,通过python调用系统命令或脚本的模块在python2有os.system,

>>> os.system('uname -a')
Darwin Alexs-MacBook-Pro.local 15.6.0 Darwin Kernel Version 15.6.0: Sun Jun  4 21:43:07 PDT 2017; root:xnu-3248.70.3~1/RELEASE_X86_64 x86_64
0 #执行状态

这条命令的实现原理是什么呢?(视频中讲,解释进程间通信的问题...)

       除了os.system可以调用系统命令,,commands,popen2等也可以,比较乱,于是官方推出了subprocess,目地是提供统一的模块来实现对系统命令或脚本的调用

       subprocess 模块允许生成新进程,连接到它们的输入/输出/错误管道,并获取它们的返回代码。 该模块旨在替换几个较旧的模块和功能:

  • os.system
  • os.spawn*

 

三种执行命令的方法

  • subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs) #官方推荐

subprocess.run(['df','-h','|','grep','disk1'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True) 
#run(列表,标准错误,标准输出)
#执行列表中的命令,如果命令顺利执行,通过PIPE管道输出给stdout,如果出错了,就把错误原因通过PIPE管道输出给stderr,这里的PIPE管道是操作系统
subprocess.run('df -h|grep disk1',shell=True) #shell=True的意思是这条命令直接交给系统去执行,不需要python负责解析
  • subprocess.call(*popenargs, timeout=None, **kwargs) #跟上面实现的内容差不多,另一种写法

  • subprocess.Popen() #上面各种方法的底层封装

call()方法

#执行命令,返回命令执行状态 , 0 or 非0
>>> retcode = subprocess.call(["ls", "-l"])

#执行命令,如果命令结果为0,就正常返回,否则抛异常
>>> subprocess.check_call(["ls", "-l"])
0

#接收字符串格式命令,返回元组形式,第1个元素是执行状态,第2个是命令结果 
>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')

#接收字符串格式命令,并返回结果
>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'

#执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res
>>> res=subprocess.check_output(['ls','-l'])
>>> res
b'total 0\ndrwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM\n'

Pope()方法

常用参数:

  • args:shell命令,可以是字符串或者序列类型(如:list,元组)
  • stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
  • preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用,在执行命令之前还可以执行一个python函数
a=subprocess.Popen('sleep 10',shell=True,stdout=subprocess.PIPE,preexec_fn=sayhi) # sayhi 是一个函数名,可在执行命令前先调用 sayhi 函数
  • shell:同上(和 run 中 shel l用法一致)
  • cwd:用于设置子进程的当前目录
a=subprocess.Popen('echo $PWD;sleep 10',shell=True,cwd='/tmp',stdout=subprocess.PIPE) 
# 先执行 'echo $PWD' 命令,再执行 'sleep 10' 命令,cwd设置子程序的当前目录
  • env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。

下面这2条语句执行会有什么区别?

a=subprocess.run('sleep 10',shell=True,stdout=subprocess.PIPE)
a=subprocess.Popen('sleep 10',shell=True,stdout=subprocess.PIPE)

       区别是Popen会在发起命令后立刻返回,而不等命令执行结果。

  • poll() 检测命令是否运行,当命令执行完成后,返回命令执行结果,如果没执行完,就没反应。
  • wait() 不想立刻返回,想等待命令执行完毕。
  • terminal() 终止所启动的进程。
  • pid() 拿到所启动进程的进程号。
  • kill() 杀死所启动的进程。
a = subprocess.Popen('for i in $(seq 1 100);do sleep 1;echo $i >>sleep.log;done',shell = True,stdout = subprocess.PIPE)

os.kill(a.pid(),signal.SIGTERM) # a.pid()获取进程号,杀死进程
a.terminal(a.pid()) #和上面的 os.kill() 的作用一致
  • communicate() 与启动的进程交互一次,发送数据到stdin,并从stdout接收输出,然后等待任务结束
>>> a = subprocess.Popen('python3 guess_age.py',stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,shell=True)
>>> a.communicate(b'22') #输入必须为 bytes 格式
(b'your guess:try bigger\n', b'')
  • send_signal(signal.xxx)发送系统信号
import signal
os.kill(36965.signal.SIGTERM)