subprocess模块

Posted 休耕

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了subprocess模块相关的知识,希望对你有一定的参考价值。

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

  执行返回命令执行状态,利用echo $?  查看到返回值‘0’

>>> import os
>>> os.system(uname -a)
Darwin MacBook-Pro.local 17.2.0 Darwin Kernel Version 17.2.0: Fri Sep 29 18:27:05 PDT 2017; root:xnu-4570.20.62~3/RELEASE_X86_64 x86_64
0

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

run()方法:subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs)

  首先注意run方法执行结果,有stdout、args、returncode这三个属性。

>>> import subprocess
>>> a = subprocess.run([du, -sh])
 24K    .
>>> a.stdout   # 拿到标准输出结果
>>> a.args      # 拿到标准输出命令
[du, -sh]
>>> a.returncode    # 拿到标准输出返回值
0

 

  标准写法:subprocess.run([‘df‘, ‘-h‘],stdout=subprocess.PIPE,stderr=subprocess.PIPE,check=True)

>>> a = subprocess.run([ls, -lrt],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
>>> a.stdout    # 标准输出
btotal 48\n-rw-r--r--  1 hqs  staff     51  4 12 16:10 __init__.py\n-rw-r--r--  1 hqs  staff  16790  4 17 22:38 ftp_client.py\n
>>> a.stderr    # 标准错误
b‘‘

>>> a = subprocess.run([df, -sdfh],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
>>> a.stderr
bdf: illegal option -- s\nusage: df [-b | -H | -h | -k | -m | -g | -P] [-ailn] [-T type] [-t] [filesystem ...]\n
>>> a.stdout
b‘‘

  运用管道借用操作系统内存,实现Python和shell内存交互。拿到shell命令执行结果。

  带管道符的情况,不要用列表(解析不了);

  shell=True不做解析直接把整个命令交给shell处理;

  check=True有错误将直接提示报错。不加的话不提示报错,需要用stderr查看错误。

>>> a = subprocess.run(df -h | grep disk1,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
>>> a.stdout
b/dev/disk1s1   233Gi   62Gi  168Gi    28%  871372 9223372036853904435    0%   /
/dev/disk1s4   233Gi  3.0Gi  168Gi     2%       3 9223372036854775804    0%   /private/var/vm\n
>>> a = subprocess.run([df,-ususidih],stdout=subprocess.PIPE,stderr=subprocess.PIPE,check=True)
提示出现CalledProcessError报错

 

CALL方法:subprocess.call(*popenargs, timeout=None, **kwargs)

  应用列表执行命令主要有call()方法和check_call()方法。

  应用列表执行命令,并返回结果:check_output()方法。

  应用字符串执行命令主要有getstatusoutput()方法和getoutput()方法。

# 执行命令,返回命令执行状态, 0或非0
>>> retcode = subprocess.call(["ls", "-l"])
total 0
drwx------+  9 huangqiushi  staff   288  3  2 13:32 Desktop
drwx------+  4 huangqiushi  staff   128  3  1 08:17 Documents
...
drwxr-xr-x   7 huangqiushi  staff   224  3  2 20:26 PycharmProjects
>>> retcode
0

# 执行命令,如果命令结果为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 = subprocess.check_output([du,-sh])
>>> res
b 27G\t.\n
Popen()方法
Popen方法是最基础的方法,是run()方法和call()方法的底层封装。
常用参数:
args:shell命令,可以是字符串或者序列类型(如:list,元组)
stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
shell:shell=True的意思是这条命令直接交给系统去执行,不需要python负责解析
cwd:用于设置子进程的当前目录
env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。

  Popen发起一个新进程不影响主程序,run是当前进程执行,Popen是后台执行。

  Popen调用后会返回一个对象,可以通过这个对象拿到命令执行结果或状态等,该对象有以下方法:

def sayhi():
    print(run...hahah)
a = subprocess.Popen(echo $PWD;sleep 2,shell=True,cwd="/tmp",stdout=subprocess.PIPE,preexec_fn=sayhi)
a.poll()  # 检查子程序是否终止,返回返回值
a.wait()  # 等待子程序终止,返回返回值
a.terminate()  # 终止所启动的进程
a.kill()    # 杀死所启动的进程
a.communicate()    # 与启动的进程交互,发送数据到stdin,从stdout接收输出,然后等待任务结束
a.send_signal(signal.xxx)   # 发送系统信号
a.pid # 拿到所启动进程的进程号

  管道操作

>>> def sayhi():
...         print(run...hahah)
>>> a = subprocess.Popen(sleep 10,shell=True,stdout=subprocess.PIPE,preexec_fn=sayhi)
>>> a.stdout  
<_io.BufferedReader name=3>
>>> a.stdout.read() 
brun...hahah\n
# cwd设置当前目录
>>> a = subprocess.Popen(echo $PWD;sleep 2,shell=True,cwd="/tmp",stdout=subprocess.PIPE,preexec_fn=sayhi)
>>> a.stdout.read()
brun...hahah\n/private/tmp\n 

  pid()方法使用,terminate()kill()方法的区别:终止和杀死

 

>>> a = subprocess.Popen(sleep 100,shell=True,stdout=subprocess.PIPE) 
>>> a.pid
14939
>>> a.terminate()  # 停掉不强制,进程没有停掉

>>> a = subprocess.Popen(for i in $(seq 1 100);do sleep 1;echo $i >> /tmp/sleep.log;done,shell=True,stdout=subprocess.PIPE)
>>> a.pid
15056
>>> a.kill()   # 强制杀死进程

 

  communicate()方法,与启动的进程交互,发送数据到stdin,并从stdout接收输出,然后等待任务结束需要用bytes进行交互、且只能交互一次。

>>> a = subprocess.Popen(python3 guessAge.py,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,shell=True)
>>> a.communicate(b1234)  # 需要和bytes进行交互
(bguess age: try smaller,you have 3 chance\n
guess age: , bTraceback (most recent call last):\n
  File "guessAge.py", line 19, in <module>\n
      guess_age = int(input("guess age: "))\n
      EOFError: EOF when reading a line\n)

  send_signal()方法发送系统信号

import signal
a = subprocess.Popen(sleep 100,stdout=subprocess.PIPE,shell=True)
a.send_signal(signal.SIGKILL)

 

 

 

 

 

 

以上是关于subprocess模块的主要内容,如果未能解决你的问题,请参考以下文章

使用 subprocess 模块是不是会释放 python GIL?

python常用代码片段总结

常用模块——subprocess模块

subprocess实用手册

subprocess 模块

python模块之subprocess模块, struct模块