• Python subprocess模块学习笔记


    一、进程和线程

    进程和线程是为了解决多任务问题而产生的概念,什么是多任务了?计算机的多任务是指计算设备同时执行不同的事情,过去CPU是单核的,如果要执行多任务,操作系统会让CPU先执行任务1 0.01秒 再执行任务2 0.01秒,接着执行任务3 0.01秒…回到执行任务1,对于单核CPU,多任务其实就是单任务,只不过切换和执行的时间很快,看起来是多任务执行。
    随着芯片技术的不断发展,现在的计算设备芯片普遍是多核CPU,所以“多任务”就是在同一时间执行不同的任务。

    而一个任务就需要一个进程来处理,操作系统会给每个任务分配一个进程,比如说你要用电脑看视频,同时又要写文档记笔记,还要用浏览器查资料,这三个任务:看视频、记笔记、查资料,操作系统会分配三个进程来对应。Windows系统的任务管理器可以很直观的表现出来:
    在这里插入图片描述

    有些进程不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部要同时做多个事情,就需要同时运行多个“子任务”,这些子任务称为-——线程。线程是最小的执行单元,每个进程由至少一个线程组成。

    多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。如果一个计算设备是多核多线程的CPU,比如4核8线程,该设备上执行多任务就是真正地同时执行。

    总结:多任务的实现方式有三种:

    • 多进程
    • 多线程
    • 多进程+多线程
      python既支持多进程,也支持多线程。其中python自带的subprocess就是支持多进程编程。

    二、python subprocess模块

    subprocess模块用来代替os.system和os.spawn*,使用该模块可以很方便地大量创建新的进程,连接输入/输出/错误管道,以及获取进程的返回值。

    python3.5 以上的版本建议使用subprocess.run()方法,python3.5以下的版本可以使用subprocess.call()

    下面我们看下subprocess.run()的参数构造:

    subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None, **other_popen_kwargs)
    
    • 1

    subprocess.run()方法的使用介绍网上已经有很多,这里就不一一介绍,主要记录下我在使用这个方法的时候遇到的一些疑难点吧
    args:需要执行的指令,可以是序列,也可以是字符串
    capture_output=True:表示stdout 和 stderr都会捕获。但是这样设置不能保证同时能获取stdout和stderr,如果想要同时获取,可以使用stdout=subprocess.PIPE, stderr=subprocess.PIPE来代替capture_out。
    timeout:timeout参数被传递给popen . communication()。如果超时过期,子进程将被杀死并等待。子进程终止后将重新引发TimeoutExpired异常。
    input:该参数会传递给popen . communication(),从而传递给子进程的标准输入。如果使用,则必须为字节序列,如果指定了encoding或errors或text为true则必须为字符串。当该参数被使用时,内部Popen对象会自动使用stdin=PIPE创建,而stdin参数可能也不会被使用。
    check:如果check为true,并且进程以非零的退出码退出,则会引发一个CalledProcessError异常。该异常的属性包含参数、退出代码以及stdout和stderr(如果它们被捕获的话)。
    encoding/errors如果encoding或errors被指定,或者text=True, stdin、stdout和stderr的文件对象将使用指定的encoding和errors以文本模式打开或者默认为io.TextIOWrapper。universal_newlines参数等价于text,提供它是为了向后兼容。默认情况下,文件对象以二进制模式打开。

    subprocess.run(["ls", "-l"])  # doesn't capture output
    CompletedProcess(args=['ls', '-l'], returncode=0)
    
    subprocess.run("exit 1", shell=True, check=True)
    Traceback (most recent call last):
      ...
    subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
    
    subprocess.run(["ls", "-l", "/dev/null"], capture_output=True)
    CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
    stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n', stderr=b'')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    注意:python3.5开始才支持subprocess.run()方法,3.6 添加了 encoding 和errors参数,3.7添加了text、capture_output参数,universar_newlines是为了更好的兼容下面的版本,它和text是等价的

    subprocess模块的方法其实都是有Popen构造函数实现的,Popen可以灵活地创建和管理进程,以便开发人员能够处理subprocess函数没有涉及的不太常见的情况。

    class subprocess.Popen(args, bufsize=- 1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, group=None, extra_groups=None, user=None, umask=- 1, encoding=None, errors=None, text=None, pipesize=- 1)
    
    • 1

    Popen()方法:
    popen.communicate(input=None, timeout=None): 进程间通信,比如adb shell进入嵌入式linux设备后,我需要在adb shell 里执行设备厂商自行设计的脚步程序,我们可以这样设计自己的代码。

    def cmd_with_record(cmd,timeout=None):
    
        log = open(logfileName,'a')
        # proc = Popen(["cmd"], shell=False, stdout=PIPE, stdin=PIPE, stderr=PIPE)
        proc = Popen(["cmd"], shell=False, stdout=log, stdin=PIPE, stderr=PIPE)
        # log.close()
        commands = cmd
        outs, errs = proc.communicate(commands.encode("gbk"),timeout=timeout)
        log.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    popen.communicate()返回一个元组(stdout_data, stderr_data), 如果text=True,则返回的是字符串类型,否则是字节数据(byte)

    参考:

    1. 官方教程-Subprocess
    2. 廖雪峰Python-多进程
    3. 油管subprocess教程
    4. 进程和线程基础知识全家桶
  • 相关阅读:
    leetcode-链表类题目
    java面试强基(10)
    股票业绩预测:实战 iwencai JS逆向 + 数据采集(20220803)
    Vite和Vue3:Vite是一种新的开发服务器和构建工具,它利用了现代浏览器支持的原生ES模块导入,为开发者提供了极速的冷启动和即时热更新
    深入理解 Django 单元测试
    2310月问题描述
    鸿蒙开发接口媒体:【@ohos.multimedia.medialibrary (媒体库管理)】
    [重庆思庄每日技术分享]-oracle EM13.4 安装失败之后的处理
    netty系列之: 在netty中使用 tls 协议请求 DNS 服务器
    Dockerfile多阶段构建(multi-stage builds)
  • 原文地址:https://blog.csdn.net/jackhh1/article/details/127601932