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,目地是提供统一的模块来实现对系统命令或脚本的调用。

一、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    # 标准输出
b'total 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
b'df: 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报错

二、subprocee.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'

三、subprocess.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是后台执行。

1、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 # 拿到所启动进程的进程号

2、管道操作

>>> 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() 
b'run...hahah\n'
# cwd设置当前目录
>>> a = subprocess.Popen('echo $PWD;sleep 2',shell=True,cwd="/tmp",stdout=subprocess.PIPE,preexec_fn=sayhi)
>>> a.stdout.read()
b'run...hahah\n/private/tmp\n' 

3、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()   # 强制杀死进程

4、communicate()方法

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

>>> a = subprocess.Popen('python3 guessAge.py',stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,shell=True)
>>> a.communicate(b'1234')  # 需要和bytes进行交互
(b'guess age: try smaller,you have 3 chance\n
guess age: ', b'Traceback (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')

5、send_signal()方法发送系统信号

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

 

 

 

 

 

 

posted @ 2018-04-18 13:31  休耕  阅读(560)  评论(0编辑  收藏  举报