zoukankan      html  css  js  c++  java
  • python重要模块之subprocess模块

    python重要模块之subprocess模块

    我们经常要通过python去执行系统的命令或者脚本,系统的shell命令是独立于你的python进程之外的,每执行一条命令,就相当于发起了一个新的进程,通过python调用系统命令或脚本的模块。

    之前我们也学到过和系统交互的模块-os模块

    In [1]: import os
    
    In [2]: os.system('uname -a')
    Linux host-10-200-137-195 3.10.0-693.21.1.el7.x86_64 #1 SMP Wed Mar 7 19:03:37 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
    Out[2]: 0  # 命令的执行返回结果,0为执行成功,非0表示失败
    

    除了so.system可以调用系统命令,commands和popen2等也可以,因为比较乱,所以官方推出了subprocess,目的就是提供统一的模块来实现对系统命令或脚本的调用。简单演示下commands的用法:
    commands模块讲解

    # commands模块,在python2中运行
    
    >>> import commands  # 导入这个模块
    >>> status,output = commands.getstatusoutput('ls')  
    >>> status  # 代表shell命令的返回状态
    0
    >>> output  # 代表shell命令你的执行结果
    'anaconda-ks.cfg'
    

    三种命令的执行方式

    • subprocess.run() # 官方推荐
    • subprocess.call() # 跟run()方法差不多,另一种写法
    • subprocess.Popen() # 上面各种方法的封装

    run()方法

    标准写法

    subprocess.run(['df','-h'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True)
    

    慢慢来讲:

    In [2]: subprocess.run(['df','-h'])  # 这个是将shell命令拆分成列表的形式写
    Filesystem               Size  Used Avail Use% Mounted on
    /dev/mapper/centos-root   50G  2.2G   48G   5% /
    devtmpfs                 3.9G     0  3.9G   0% /dev
    tmpfs                    3.9G     0  3.9G   0% /dev/shm
    tmpfs                    3.9G  8.4M  3.9G   1% /run
    tmpfs                    3.9G     0  3.9G   0% /sys/fs/cgroup
    /dev/vda1                509M  175M  335M  35% /boot
    tmpfs                    783M     0  783M   0% /run/user/0
    Out[2]: CompletedProcess(args=['df', '-h'], returncode=0)  # 返回了一个对象
    
    # 赋值
    a = subprocess.run(['df','-h'])  # 此时a就是返回的那个对象,通过这个对象,我们可以使用很多方法
    
    In [3]: a  # 就是返回的那个对象
    Out[3]: CompletedProcess(args=['df', '-h'], returncode=0)
    
    In [4]: a.returncode  # 查看shell命令执行状态
    Out[4]: 0
    
    In [5]: a.args  # 查看所带的参数
    Out[5]: ['df', '-h']
    
    In [6]: a.check_returncode  # 判断命令执行状态是否为0,不为0则报错
    Out[6]: <bound method CompletedProcess.check_returncode of CompletedProcess(args=['df', '-h'], returncode=0)>
    
    In [7]: a.stdout
    
    In [8]: a.stderr
    

    那么问题来了,为什么a.stdout和a.stderr没有输出任何东西呢?让我们来看看subprocess模块内部的工作机制吧?

    通过python去执行Liunx命令,相当于发起了一个新的进程,我们比如: 在Word文档中把QQ打开了,word要占用内存,QQ也要占用内存,那么在word中能不能给QQ里的好友发送消息呢?
    答案是不能的,因为:如果wold可以去访问QQ的内存数据,那么就可以通过QQ发送数据了,那么就会有一个问题出现,QQ被搞崩了,所以操作系统为了避免这样的事情发生,就严格的限制了每个进程在内存中都是独立的,我们再打个比方:在浏览器中复制粘贴,其实就是通过操作系统去实现的,操作系统也有着自己的内存,从浏览器中赋值的内容,然后通过操作系统导给QQ等,操作系统可以访问每个进程的内存。
    python和shell的关系就是这样,subprocess帮我们发起了一个进程然后得到结果,python和shell之间是不互通的,如果想要互通,那么就需要有个桥梁,即python和操作系统之间的桥梁。
    所以就会有这么一种写法了

    subprocess.run([ls'','-l'],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    其中PIPE就相当于是桥梁,stdout和stderr被称为标准输出
    # 此时让我们再看一下运行结果
    In [9]: a = subprocess.run(['ls','-l'],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    
    In [10]: a.stdout  # 标准输出
    Out[10]: b'total 4
    -rw-------. 1 root root 1224 Oct 15  2016 anaconda-ks.cfg
    '
    
    In [11]: a.stderr  # 标准错误输出
    Out[11]: b''
    

    继续往下讲,在run()方法中,check=True是啥意思呢?写代码看下:

    In [13]: a = subprocess.run(['ls','-ldada'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,check=True)  # 故意设置shell命令是错误的,结果并不检查
        ...: 
    
    In [14]: a = subprocess.run(['ls','-lkjh'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,check=True)  # 检查shell命令是否正确,不正确则报错
    ---------------------------------------------------------------------------
    CalledProcessError                        Traceback (most recent call last)
    <ipython-input-14-0e63d8200326> in <module>()
    ----> 1 a = subprocess.run(['ls','-lkjh'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,check=True)
    
    /usr/local/lib/python3.6/subprocess.py in run(input, timeout, check, *popenargs, **kwargs)
        416         if check and retcode:
        417             raise CalledProcessError(retcode, process.args,
    --> 418                                      output=stdout, stderr=stderr)
        419     return CompletedProcess(process.args, retcode, stdout, stderr)
        420 
    
    CalledProcessError: Command '['ls', '-lkjh']' returned non-zero exit status 2.
    
    In [15]: a = subprocess.run(['ls','-lkjh'],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    
    In [16]: a = subprocess.run(['ls','-lkjh'],stdout=subprocess.PIPE,stderr=subprocess.PIPE),check=True
      File "<ipython-input-16-15adea8e8d2f>", line 1
        a = subprocess.run(['ls','-lkjh'],stdout=subprocess.PIPE,stderr=subprocess.PIPE),check=True
           ^
    SyntaxError: can't assign to function call
    

    如果想执行复杂的shell命令,让我们来看看怎么写?

    In [17]: a = subprocess.run(['df','-h','|','grep','Used'],stdout=subprocess.PIPE,stderr=subprocess.PI
        ...: PE)  # 刚刚的ls -l不就是这样的嘛一直拼参数怎么会报错了呢
    
    In [18]: a.stderr  # 我知道是错误的,故意直接写的是标准错误输出
    Out[18]: b'df: xe2x80x98|xe2x80x99: No such file or directory
    df: xe2x80x98grepxe2x80x99: No such file or directory
    df: xe2x80x98Usedxe2x80x99: No such file or directory
    '
    
    # 其实应该这样写
    In [21]: a = subprocess.run('df -h | grep Used',stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
    
    In [22]: a.stdout
    Out[22]: b'Filesystem               Size  Used Avail Use% Mounted on
    '
    
    In [23]: a.stderr
    Out[23]: b''
    # shell=True这样写就是告诉subprocess,你之前帮我拼参数,但是现在不用了,只需要把整条命令传递个shell去处理就行了
    

    call()方法

    废话不多说,直接看代码

    In [24]: subprocess.call(['ls','-l'])
    total 4
    -rw-------. 1 root root 1224 Oct 15  2016 anaconda-ks.cfg
    Out[24]: 0  # 命令执行的返回状态,0代表成功
    
    # 执行命令,如果命令结果为0,就正常返回,否则抛异常
    In [25]: subprocess.check_call(['ls','-l'])
    total 4
    -rw-------. 1 root root 1224 Oct 15  2016 anaconda-ks.cfg
    Out[25]: 0
    
    In [26]: subprocess.check_call(['ls','-lhgj'])
    ls: invalid option -- 'j'
    Try 'ls --help' for more information.
    ---------------------------------------------------------------------------
    CalledProcessError                        Traceback (most recent call last)
    <ipython-input-26-c38033ac38dc> in <module>()
    ----> 1 subprocess.check_call(['ls','-lhgj'])
    
    /usr/local/lib/python3.6/subprocess.py in check_call(*popenargs, **kwargs)
        289         if cmd is None:
        290             cmd = popenargs[0]
    --> 291         raise CalledProcessError(retcode, cmd)
        292     return 0
        293 
    
    CalledProcessError: Command '['ls', '-lhgj']' returned non-zero exit status 2.
    
    # 接受字符串格式的命令,返回元组形式,第1个元素是执行状态,第2个是命令结果
    In [27]: subprocess.getstatusoutput('ls /bin/ls')
    Out[27]: (0, '/bin/ls')
    
    # 接收字符串格式命令,并返回结果
    In [29]: subprocess.getoutput('df -h | grep Used')
    Out[29]: 'Filesystem               Size  Used Avail Use% Mounted on'
    
    # 执行命令并返回结果,注意是返回结果,不是打印,
    
    

    Popen()方法

    常用参数

    • args:shell命令,可以是字符或者序列化类型(list,tuple)
    • stdint,stdout,stderr:分别表示程序的标准输入、输出、错误
    • preexec_fn:只在Unix平台下有效,用于指定一个可执行对象,它将在子进程运行之前被调用
    • shell:同上
    • cwd:用于指定子进程的当前目录
    • env:用于指定子进程的环境变量,如果env=None,子进程的环境变量将会从父进程中继承

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

    In [7]: a = subprocess.call('sleep 10',shell=True,stdout=subprocess.PIPE)  # 它会一直等待着这段代码执行完才结束
    
    In [8]: a = subprocess.Popen('sleep 10',shell=True,stdout=subprocess.PIPE)  # 相当于是有两个进程,主程序继续下一步行动,其余则另开一个新的进程执行sleep 10
    

    如果你调用的命令或脚本需要哦执行10分钟,你的主程序不需卡在哪里等待10分钟,可以继续往下走干别的事情,每过一会,就可以通过poll()方法去检测一下这个命令是否执行完成。
    Popen调用后会返回一个对象,可以通过这个对象拿到命令的执行结果或状态等,该对象有以下方法:
    1.poll():查看这个进程的执行状态,没有执行完则不输出,执行正确输出0,错误非0

    In [13]: a = subprocess.Popen('sleep 10',shell=True,stdout=subprocess.PIPE)
    
    In [14]: a.poll()
    

    2.wait():等待这个进程结束,知道返回运行结果

    In [15]: a = subprocess.Popen('sleep 10',shell=True,stdout=subprocess.PIPE)
    
    In [16]: a.wait()
    Out[16]: 0
    

    3.terminate():终止所启动的进程
    4.kill():杀死所启动的进程

    In [24]: a = subprocess.Popen('for i in $(seq 1 100);do sleep 1;echo $i >> sleep1.log;done',shell=True,stdou
        ...: t=subprocess.PIPE)
    
    In [25]: a.terminate  # 这个是内存地址
    Out[25]: <bound method Popen.terminate of <subprocess.Popen object at 0x7f49d85717f0>>
    
    In [26]: a.terminate()  # 杀死进程的一种方法
    
    In [28]: a = subprocess.Popen('for i in $(seq 1 100);do sleep 1;echo $i >> sleep1.log;done',shell=True,stdou
        ...: t=subprocess.PIPE)
    
    In [29]: a.kill()  # 杀死进程的另一种方法
    
    

    5.pid():获取子进程的pid

    In [30]: a = subprocess.Popen('for i in $(seq 1 100);do sleep 1;echo $i >> sleep1.log;done',shell=True,stdou
        ...: t=subprocess.PIPE)
    
    In [33]: a.pid
    Out[33]: 4931
    

    现在讲一下Popen方法的其中几个参数

    # cwd设置子进程的当前目录
    In [4]: a = subprocess.Popen('echo $PWD;sleep 2',shell=True,cwd='/tmp',stdout=subprocess.PIPE)
    
    In [5]: a.stdout.read()
    Out[5]: b'/tmp
    '
    
    # preexec_fn=  在子进程运行之前被调用
    
    # 最后一个,与程序的交互,communicate(),发送数据到stdin,并从stdout接收输入,然后等待任务结束
    
    这是一个猜数字的游戏1.py
     11 import random
     12      
     13 num = random.randint(1,100)
     14 your_guess = int(input('your_guess:'))
     15      
     16 if your_guess > num:
     17     print('bigger')
     18 elif yur_guess < num:
     19     print('smaller')
     20 else:
     21     print('ok')   
    
    程序交互:
    >>> import subprocess
    >>> a = subprocess.Popen('python3 1.py',shell=True,stdout=subprocess.PIPE)
    然后:
    a.communicate(b'22')  # 就可以了,我的xshell一运行就断开连接,所以,我就不演示啦。
    
  • 相关阅读:
    Vim的分屏功能 | 酷壳 CoolShell.cn
    分享:Hadoop的Python框架指南
    KMP算法之另类图示分析
    C#cookie自动获取工具发布
    tmux Tutorial Split Terminal Windows Easily
    爱上MVC3~将系统的路由设置抽象成对象吧
    DDD~microsoft NLayerApp项目中的层次结构图
    不说技术~难得糊涂
    DDD~基础设施层
    基础才是重中之重~开发人员应用学会用throw
  • 原文地址:https://www.cnblogs.com/xiaoyafei/p/9043288.html
Copyright © 2011-2022 走看看