zoukankan      html  css  js  c++  java
  • python3中使用subprocess模块执行外部命令

    一. subprocess模块介绍

    1. subprocess模块可以替代os模块下的os.system和os.popen等操作方法

    2. subprocess模块在python2和python3上的使用上有一定的区别,本文主要介绍的是在python3.6上的使用

    3. subprocess模块的作用是执行外部命令(支持同步执行和异步执行),可以返回执行状态码,也可以返回执行内容

    4. subprocess模块的方法有很多,最核心的方法为subprocess.Popen方法python3中如果只需要同步执行优先使用subprocess.run方法

    二. subprocess.run()方法的介绍

    2.1 执行代码:在windows下执行一条cmd命令

    # -*- coding:utf-8 -*-
    # Author:chinablue
    
    import subprocess
    
    # 在windows下执行cmd命令:echo hello dj
    p = subprocess.run("echo hello dj", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print(f'获取返回对象: {p}')
    print(f'获取执行命令:{p.args}')
    print(f'获取返回码:{p.returncode}')
    print(f'获取返回数据:{p.stdout}')

    2.2 执行结果:

    获取返回对象: CompletedProcess(args='echo hello dj', returncode=0, stdout=b'hello dj
    ', stderr=b'')
    获取执行命令:echo hello dj
    获取返回码:0
    获取返回数据:b'hello dj
    '

    2.3 分析小结:

    1) subprocess.run()方法是一个同步方法,执行后会返回一个CompletedProcess对象

    2) 通过管道可以捕获子进程的标准输出(stdout)和标准错误(stderr)

        如果使用的是python3.7及以上版本,可以使用capture_output=True参数替换stdout=subprocess.PIPE和stderr=subprocess.PIPE参数

    p = subprocess.run("echo hello dj", shell=True, capture_output=True)
    print(f'获取返回数据:{p.stdout}')
    # 执行结果:获取返回数据:b'hello dj
    '

        将执行结果输出到一个文件中去

    # 将命令的执行结果输出到output.txt文件中
    with open("output.txt", "w") as f:
        p1 = subprocess.run("dir", shell=True, stdout=f, universal_newlines=True, encoding="utf-8")

    3) 默认情况下,获取的返回数据(stdxxx)为字节类型。

        通过universal_newlines=True参数可以让返回数据以文本字符串输出

    p = subprocess.run("echo hello dj", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    print(f'获取返回数据:{p.stdout}')
    # 运行结果:获取返回数据:hello dj

        如果使用的是python3.7及以上版本, 可以使用text=True参数替换universal_newlines=True参数

    p = subprocess.run("echo hello dj", shell=True, capture_output=True, text=True)
    print(f'获取返回数据:{p.stdout}')
    # 运行结果:获取返回数据:hello dj

        如果不使用universal_newlines=True参数或text=True参数,我们也可以对p.stdout进行解码转换

    p = subprocess.run("echo hello dj", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print(f'获取返回数据:{p.stdout.decode("gbk")}')  # 注意:windows下需要使用gbk解码
    # 运行结果:获取返回数据:hello dj

     通过splitlines()函数, 可以让返回数据以列表输出

    p = subprocess.run("echo hello dj", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print(f'获取返回数据:{p.stdout.decode("gbk").splitlines()}')  # 注意:windows下需要使用gbk解码
    # 运行结果:获取返回数据:['hello dj']

    4) 关于返回状态码的说明

         如果命令执行成功,returncode返回0; 如果命令执行失败,returncode返回负值

         check=True参数会自动检查p.returncode是否返回0, 如果不是0就抛出subprocess.CalledProcessError异常

    p = subprocess.run("echo1", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
    print(f'获取返回数据:{p.stdout}')

        如果不使用 check=True参数,我们可以通过p.returncode来做判断条件, 进而打印出详细错误p.stderr

    p = subprocess.run("echo1", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    if p.returncode !=0:
        print(f'命令执行失败:{p.stderr}')

    三. subprocess.Popen()方法的介绍

    subprocess.run()是通过subprocess.Popen()来实现的
    subprocess.Popen()可以异步执行

    3.1 执行代码:在windows下执行一条cmd命令

    # -*- coding:utf-8 -*-
    # Author:chinablue
    
    import subprocess
    
    # 在windows下执行cmd命令:echo chinablue
    p = subprocess.Popen("echo chinablue", shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    print(f"获取返回对象: {p}")
    print(f"获取pid: {p.pid}")
    print(f"获取返回数据: {p.stdout.read()}")
    p.wait()
    print(f"获取执行状态: {p.poll()}")

    3.2 执行结果:

    获取返回对象: <subprocess.Popen object at 0x0028C4B0>
    获取pid: 5260
    获取返回数据: chinablue
    获取执行状态: 0

    3.3 分析小结:

    1) subprocess.Popen()方法是一个异步方法,执行后会返回一个Popen对象

    2) 获取子进程的标准输出(stdout)和标准错误(stderr)

        方式1:通过p.stdoutp.stderr获取,如上例所示

        方式2:通过p.communicate()获取,它返回一个元祖,元祖的第1个元素为stdout内容,元祖的第2个元素为stderr内容

    p = subprocess.Popen("echo chinablue", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    print(f"获取返回数据: {p.communicate()[0]}")
    print(f"获取返回错误: {p.communicate()[1]}")

     3) 获取命令的执行状态:p.poll()

        如果执行完subprocess.Popen()方法后,立即获取命令的执行状态, 则返回结果为None,因为此时子进程扔在运行中

        如果我们需要等待子进程运行完毕后,再去获取命令的执行状态。那么我们可以使用p.wait()方法,这相当于将默认的异步操作改为同步操作

    p = subprocess.Popen("echo chinablue", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    print(p.poll())  # 运行结果: None
    p.wait() 
    print(p.poll())  # 运行结果: 0

    四. 常见场景举例

    场景1:在windows下,执行ipconfig命令并对结果进行过滤

    # -*- coding:utf-8 -*-
    # Author:chinablue
    
    import subprocess
    
    # 步骤1:将ipconfig命令的执行结果写入output.txt文件
    with open("output.txt", "w") as f:
        subprocess.run("ipconfig", shell=True, stdout=f, universal_newlines=True, encoding="utf-8")
    # 步骤2:将p1的输出内容作为p2的输入内容
    p1 = subprocess.run("type output.txt", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    p2 = subprocess.run("findstr IPv4", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, input=p1.stdout)
    print(p2.stdout)

    场景2:在windows下,模拟并发执行命令

    # -*- coding:utf-8 -*-
    # Author:chinablue
    
    import time
    import subprocess
    
    # 模拟一条耗时命令,此命令在windows下执行相当于sleep 2s
    cmd = "ping -n 3 127.0.0.1 > nul"
    
    start_time = time.time()
    p1 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    p2 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    p1.wait()
    p2.wait()
    print(f"运行时间共计:{int(time.time() - start_time)}s")
    # p1和p2命令执行各需耗时2s,通过并发执行后共计耗时也是2s

    场景3:自动处理命令行交互

    # -*- coding: utf-8 -*-
    # @Author  : chinablue
    
    import subprocess
    
    # 这是一条对视频文件进行处理的命令(改变视频分辨率,同时将视频设置为倍速播放)
    cmd = f'ffmpeg -i "dj_xiaomi.mp4" -s vga -filter:v "setpts=0.5*PTS" output.mp4'
    p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
    
    # 命令在执行过程中,如果本地已经存在有output.mp4文件, 那么命令行处会出现提示符: File 'output.mp4' already exists. Overwrite? [y/N]
    buff = ''
    while True:
        output = p.stdout.read(1)
        if output:
            buff += output
        else:
            break
        # 出现提示符([y/N])后,程序可以自行处理此交互行为
        if buff.endswith("[y/N]"):
            p.communicate(input="y")
            break

    另外python中的pexpect模块更擅长解决命令行的自动交互问题

  • 相关阅读:
    I Show
    Centos上安装Google Authenticator
    linux 上mailx通过465端口发送邮件
    Win10 RDP CredSSP报错处理
    linux 双网卡代理上网
    English trip EM3-LP2B Teacher:Taylor
    【BZOJ1984】月下“毛景树”-树链剖分
    c++ 数据对拍器
    【BZOJ2157】旅游-树链剖分
    游戏-博弈论-树形DP
  • 原文地址:https://www.cnblogs.com/reconova-56/p/13831624.html
Copyright © 2011-2022 走看看