python 标准类库-并行执行之subprocess-子进程管理

简介: python 标准类库-并行执行之subprocess-子进程管理

标准类库-并行执行之subprocess-子进程管理

 

1.使用subprocess模块

以下函数是调用子进程的推荐方法,所有使用场景它们都能处理。也可用Popen以满足更高级的使用场景

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)

运行args描述的命令,等待命令完成后返回returncode属性。

 

timeout参数会传递Popen.wait()。如果超过timeout,子进程将会被kill掉,并再次等待。子进程被终止后会抛出TimeoutExpired异常。

 

Eg:

>>>returncode = subprocess.call('exit 1', shell=True)
print(returncode)# 输出1


>>> returncode = subprocess.call('exit 0', shell=True)
print(returncode)# 输出0

 

注意:针对该函数,不要使用stdout=PIPEstderr=PIPE。因为不是从当前进程中读取管道(pipe),如果子进程没有生成足够的输出来填充OS的管道缓冲区,可能会阻塞子进程。

 

 

subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)

运行携带参数的命令,等待命令完成。如果返回代码为0,则返回,否则抛出CalledProcessError。返回代码将被赋值给CalledProcessErrorreturncode属性

 

timeout参数会传递Popen.wait()。如果超过timeout,子进程将会被kill掉,并再次等待。子进程被终止后会抛出TimeoutExpired异常。

 

Eg:

>>> subprocess.check_call(["ls", "-l"]) # run on linux only

0

 

>>> subprocess.check_call('exit 0', shell=True)

0

 

>>> subprocess.check_call('exit 1', shell=True)

Traceback (most recent call last):

……

subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

 

 

注意:针对该函数,不要使用stdout=PIPEstderr=PIPE。因为不是从当前进程中读取管道(pipe),如果子进程没有生成足够的输出来填充OS的管道缓冲区,可能会阻塞子进程。

 

 

subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)

运行携带参数的命令,并返回输出

如果返回代码为b不为0,则抛出CalledProcessError。返回代码将被赋值给CalledProcessErrorreturncode属性,且任意输出将会被存放在output属性。

timeout参数会传递Popen.wait()。如果超过timeout,子进程将会被kill掉,并再次等待。子进程被终止后会抛出TimeoutExpired异常。

Eg:

>>> subprocess.check_output(['echo', 'hello world'], shell=True)

b'"hello world"\r\n'

 

>>> subprocess.check_output(['echo', 'hello world'], universal_newlines=True, shell=True)

'"hello world"\n'

 

>>> subprocess.check_output('exit 1', shell=True)

Traceback (most recent call last):

……

subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

>>>

 

 

默认的,该函数会返回编码的字节。实际输出的编码可能依赖被调用的命令。所以,对于输出text的解码经常需要在应用层处理。可通过设置universal_newlinesTrue来覆盖编码行为。

 

也可以通过使用stderr=subprocess.STDOUT在结果中捕获标准错误。

Eg:

 

>>> subprocess.check_output(

       'dir non_existent_dir | exit 0',

       stderr=subprocess.STDOUT,

       universal_newlines=True,

       shell=True)

'找不到文件\n'

 

 

注意:针对该函数,不要使用stderr=PIPE。因为不是从当前进程中读取管道(pipe),如果子进程没有生成足够的输出来填充OS的管道缓冲区,可能会阻塞子进程。

 

 

subprocess.DEVNULL

可用于Popen函数stdinstdout或者stderr参数的特定值,表示使用指定文件os.devnull

 

subprocess.PIPE

可用于Popen函数stdinstdout或者stderr参数的指特定值,表示必须打开一个指向标准流的管道。

 

 

subprocess.STDOUT

可用于Popen函数stdinstdout或者stderr参数的指特定值,表示标准错误信息必须一起写入同样的句柄,比如标准输出。

 

exception subprocess.SubprocessError

来自该模块的所有异常的父类。

 

exception subprocess.TimeoutExpired

SubprocessError的子类,当等待子进程timeout超时抛出

 

cmd

用于衍生子进程的命令。

 

timeout

以秒wield单位的超时时间。

 

output

如果异常由check_output抛出,则存放子进程的输出。否则None

 

exception subprocess.CalledProcessError

SubprocessError的子类,当check_call()check_output()运行的进程退出时,返回非0值时抛出。

 

returncode

子进程的退出状态

 

cmd

用于衍生子进程的命令。

 

output

如果异常由check_output抛出,则存放子进程的输出。否则None

 

2.频繁使用的参数

以下是Popencallcheck_callcheck_output等函数最常使用的参数:

args所有调用的必填参数,参数值为字符串、序列。处于方便,通常更偏向于提供序列。如果传递的是单一字符串,shell参数值必须为True,如果不提供其它任何参数,传递单一字符串的情况下,该字符串必须是需要执行的程序名。

 

stdin, stdoutstderr分别指明了被执行程序的标准输入,标准输出和标准错误处理文件句柄。可选值PIPEDEVNULL,已存在文件描述符(一个正整数),已存在文件对象,NonePIPE表示应该创建通往子进程的管道。DEVNULL表示应该使用指定文件os.devnull。默认参数None则表示无进行重定向,子进程文件句柄从父进程继承。此外,stderr还可以是STDOUT,表明子进程的错误数据应该被放进相同的文件句柄stdout

 

如果universal_newlinesTrue,文件对象stdinstdoutstderr将按universal newlines(Unix行结束符:'\n',  Windows行结束符:'\r\n')模式,使用locale.getpreferredencoding(false)(函数会根据用户偏好设置,返回使用的文本数据的编码)返回的编码,以文件流的方式打开。

 

如果shellTrue,指定命令将通过shell执行。出于安全考虑,如果命令字符串参数需要通过外部的输入来构成的时候,强烈建议设置shell=False,不然容易造成shell注入之类的,如下

 

from subprocess import call

if __name__ == '__main__':

dirname = input('which dir would you like to cd in?\n')

   call('cd ' + dirname, shell=True)

 

运行结果

 

3.Popen构造器

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=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=())

在一个新进程中执行子进程。类似在Unix上使用os.execvp()Windows上使用CreateProcess()函数。

 

args参数值为字符串、序列。默认的,如果args是个序列,程序会执行args中第一项。如果args是字符串则根据平台而异,如下所述。无特殊需求,建议传递序列。

 

注意:可用shlex.split()来判断正确的args分割,特别是复杂的情况

Eg:

>>> import shlex, subprocess

>>> command_line = input()

/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"

>>> args = shlex.split(command_line)

>>> print(args)

['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "echo '$MONEY'"]

>>> subprocess.Popen(args)

 

 

Windows上,如果args为序列,那么将会按照以下规则进行转换为一个字符串,因为后台函数CreateProcess()的操作依赖字符串。

  1. 参数由空白字符(空格或tab)分隔。
  2. 通过双引号标记的字符串被解释为单个参数,不管字符串中是否包含空白字符。
  3. \"被当作是字面字符",即转义字符
  4. 除非\后面跟了双引号,如\",否则还是被解释为字面字符\
  5. \\被解释为\\\\"则被解释为一个\和一个"字符

shell参数用于指明是否使用shell作为执行程序。默认False。如果sellTrue,则推荐传递字符串参数给args

 

Unix操作系统上,shell=Trueshell默认为/bin/sh。如果args为字符串,则字符串指明了需要通过shell执行的命令。这意味着字符串必须具备准确的格式,正如在shell终端中输入的一样。如果args为序列,则第一项指定命令字符串,其它额外项则被当作额外参数,等同于说

Popen(['/bin/sh', '-c', args[0], args[1], ...])

 

Windows如果shell=TrueCOMSPCE环境变量指定了默认的shell。仅在command命令需通过shell执行,比如dir,copy命令时,使用shell=True。不必要通过设置shell=True,来运行一批处理或者基于控制的可执行程序。

 

bufsize当创建stdin/stdout/stderr管道文件对象时,bufsize将作为io.open()函数的对应的参数: 0 -意味着未缓冲means unbuffered (read and write are one system call and can return short)

1 -意味着行缓冲(means line buffered

任意正数-使用缓冲,缓冲大小和给定正数大致相等。

任意负数-使用缓冲,缓冲大小等于系统自带的o.DEFAULT_BUFFER_SIZE

 

Executable

 

executable参数指定了用于执行的替代程序。很少用到。

 

stdin, stdoutstderr

分别指定被执行程序的标准输入,标准输出,标准错误文件句柄。合法值为PIPE,DEVNULL,已存在文件描述符(一个正整数),已存在文件对象和NonePIPE表示应该创建通往子进程的管道。DEVNULL表示应该使用指定文件os.devnull。默认参数None则表示无进行重定向,子进程文件句柄从父进程继承。此外,stderr还可以是STDOUT,表明子进程的错误数据应该被放进相同的文件句柄stdout

 

preexec_fn

如果preexec_fn被设置为可调用对象,该对象将在子进程执行之前被执行(仅限Unix)。

 

close_fds

如果close_fdsTrue,所有文件描述符,012除外都在子进程执行前被关闭(仅限Unix)。(Unix only).默认值根据平台而异。Unix平台总是TrueWindows平台,当stdin/stdout/stderrNone时,为True,否则为FalseWindows平台,如果close_fdsTrue,那么子进程不会继承任何句柄。

 

universal_newlines

如果universal_newlinesTrue,文件对象stdinstdoutstderr将按universal newlines(Unix行结束符:'\n',  Windows行结束符:'\r\n')模式,使用locale.getpreferredencoding(false)(函数会根据用户偏好设置,返回使用的文本数据的编码)返回的编码,以文件流的方式打开。

 

……

 

可配合with使用,退出时,先关闭标准文件描述符,如下

import subprocess

 

if __name__ == '__main__':
with subprocess.Popen(['dir'], stdout=subprocess.PIPE, shell=True, universal_newlines = True) as proc:
print(proc.stdout.read())

 

输出

 

更多参考官方文档

 

4.Popen对象

Popen类实例有以下几个方法

 

Popen.poll()

检测子进程是否中断,设置并返回returncode

 

Popen.wait(timeout=None)

等待子进程终止,设置并返回returncode。如果进程在timeout(单位秒)之后依然没终止,则抛出TimeoutExpired异常,可以捕获该异常并再次尝试等待。

 

警告

当使用stdout=PIPE and/or stderr=PIPE时,如果子进程生成足够的输出到管道,这会阻止操作系统管道缓冲区接收更多数据,进而造成死锁。为了避免该事件,使用communicate()

 

Popen.communicate(input=None, timeout=None)

process交互:发送数据到stdin,从stdout,stderr读取数据,直到文件结束符。等待子进程终止。

 

input:可选参数,参数值为发送给子进程的数据,如果不需要发送数据,则为None。如果universal_newlinesFalse,则input数据类型必须为字节,否则可为字符串。

 

函数返回一个元组(stdoutdata, stderrdata)

 

注意,如果想发送数据到进程管道,必须在创建Popen对象时使用stdin=PIPE,类似的如果想从结果元组中获取非None值数据,创建Popen对象时需要提供stdout=PIPE and/or stderr=PIPE参数。

 

如果进程在timeout(单位秒)之后依然没终止,则抛出TimeoutExpired异常,(Python3.3.2中发丝。捕获该异常并重试comunicate,不会丢失任何输出。

 

如果超过timeout,子进程不会被kill掉,所以为了完成交互,恰当的清理友好执行的程序,应该kill子进程。


import subprocess

 

if __name__ == '__main__':
with subprocess.Popen(['dir'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, universal_newlines = True) as proc:
try:

           outs, errs = proc.communicate(timeout=15) #超时时间为15
print(outs, errs)
except subprocess.TimeoutExpired:

           proc.kill()

           outs, errs = proc.communicate()
print(outs, errs)

 

 

 

注意:读取的数据缓存在内存,所以如果数据太大或者无限制,不要使用该函数。

 

Popen.send_signal(signal)

发送signal给子进程

 

Popen.terminate()

停止子进程。

 

Popen.kill()

Kill子进程。Posix操作系统:函数会发送SIGKILL给子进程。Windowskill()terminate()别名。

 

以下为属性:

注意:使用communicate()而非.stdin.write, .stdout.read或者.stderr.read以避免死锁。

 

Popen.stdin

如果stdin参数为PIPE,该属性为给子进程提供输入的文件对象,否则为None.

 

Popen.stdout

如果stdin参数为PIPE,该属性为给子进程提供正确输出的文件对象,否则为None.

 

Popen.stderr

如果stdin参数为PIPE,该属性为给子进程提供错误输出的文件对象,否则为None.

 

Popen.pid

子进程的ID

 

注意:如果设置了shell=True,则该属性值为衍生的shell进程的id

 

Popen.returncode

子进程返回代码,如果值为None表明进程还没终止。负值-N表示子进程通过signal N终止的(仅限Unix)

 

Eg:

import sys
import  subprocess

def run_command():

   cmd = [sys.executable, 'py1.py']

   proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines = True, stderr=subprocess.STDOUT)

   outs, errs = proc.communicate()
# print(proc.communicate()) #报错,因为文件已经关闭
print(outs)

if __name__ == '__main__':

   run_command()

 

运行结果:

 

 

注:py1.pystudy.py在同一个目录下,内容如下

#!/usr/bin/env python

# -*- coding:utf-8 -*-

__author__ = 'laiyu'

print('output from py1.py')

 

5.替换老函数

可用callPopen替换一些老函数如os.system()os.spawn家族系列,shell管道等,具体参考官方文档

目录
相关文章
|
2天前
|
负载均衡 Java 调度
探索Python的并发编程:线程与进程的比较与应用
本文旨在深入探讨Python中的并发编程,重点比较线程与进程的异同、适用场景及实现方法。通过分析GIL对线程并发的影响,以及进程间通信的成本,我们将揭示何时选择线程或进程更为合理。同时,文章将提供实用的代码示例,帮助读者更好地理解并运用这些概念,以提升多任务处理的效率和性能。
|
15天前
|
消息中间件 安全 Kafka
Python IPC机制全攻略:让进程间通信变得像呼吸一样自然
【9月更文挑战第12天】在编程领域,进程间通信(IPC)是连接独立执行单元的关键技术。Python凭借简洁的语法和丰富的库支持,提供了多种IPC方案。本文将对比探讨Python的IPC机制,包括管道与消息队列、套接字与共享内存。管道适用于简单场景,而消息队列更灵活,适合高并发环境。套接字广泛用于网络通信,共享内存则在本地高效传输数据。通过示例代码展示`multiprocessing.Queue`的使用,帮助读者理解IPC的实际应用。希望本文能让你更熟练地选择和运用IPC机制。
38 10
|
11天前
|
监控 Ubuntu API
Python脚本监控Ubuntu系统进程内存的实现方式
通过这种方法,我们可以很容易地监控Ubuntu系统中进程的内存使用情况,对于性能分析和资源管理具有很大的帮助。这只是 `psutil`库功能的冰山一角,`psutil`还能够提供更多关于系统和进程的详细信息,强烈推荐进一步探索这个强大的库。
26 1
|
14天前
|
Python
惊!Python进程间通信IPC,让你的程序秒变社交达人,信息畅通无阻
【9月更文挑战第13天】在编程的世界中,进程间通信(IPC)如同一场精彩的社交舞会,每个进程通过优雅的IPC机制交换信息,协同工作。本文将带你探索Python中的IPC奥秘,了解它是如何让程序实现无缝信息交流的。IPC如同隐形桥梁,连接各进程,使其跨越边界自由沟通。Python提供了多种IPC机制,如管道、队列、共享内存及套接字,适用于不同场景。通过一个简单的队列示例,我们将展示如何使用`multiprocessing.Queue`实现进程间通信,使程序如同社交达人般高效互动。掌握IPC,让你的程序在编程舞台上大放异彩。
13 3
|
16天前
|
安全 开发者 Python
Python IPC大揭秘:解锁进程间通信新姿势,让你的应用无界连接
【9月更文挑战第11天】在编程世界中,进程间通信(IPC)如同一座无形的桥梁,连接不同进程的信息孤岛,使应用无界而广阔。Python凭借其丰富的IPC机制,让开发者轻松实现进程间的无缝交流。本文将揭开Python IPC的神秘面纱,介绍几种关键的IPC技术:管道提供简单的单向数据传输,适合父子进程间通信;队列则是线程和进程安全的数据共享结构,支持多进程访问;共享内存允许快速读写大量数据,需配合锁机制确保一致性;套接字则能实现跨网络的通信,构建分布式系统。掌握这些技术,你的应用将不再受限于单个进程,实现更强大的功能。
36 5
|
16天前
|
消息中间件 Kafka 数据安全/隐私保护
Python IPC实战指南:构建高效稳定的进程间通信桥梁
【9月更文挑战第11天】在软件开发中,随着应用复杂度的提升,进程间通信(IPC)成为构建高效系统的关键。本文通过一个分布式日志处理系统的案例,介绍如何使用Python和套接字实现可靠的IPC。案例涉及定义通信协议、实现日志发送与接收,并提供示例代码。通过本教程,你将学会构建高效的IPC桥梁,并了解如何根据需求选择合适的IPC机制,确保系统的稳定性和安全性。
32 5
|
2天前
|
存储 算法 Java
关于python3的一些理解(装饰器、垃圾回收、进程线程协程、全局解释器锁等)
该文章深入探讨了Python3中的多个重要概念,包括装饰器的工作原理、垃圾回收机制、进程与线程的区别及全局解释器锁(GIL)的影响等,并提供了详细的解释与示例代码。
9 0
|
2天前
|
调度 Python
python3多进程实战(python3经典编程案例)
该文章提供了Python3中使用多进程的实战案例,展示了如何通过Python的标准库`multiprocessing`来创建和管理进程,以实现并发任务的执行。
12 0
|
5天前
|
并行计算 API 调度
探索Python中的并发编程:线程与进程的对比分析
【9月更文挑战第21天】本文深入探讨了Python中并发编程的核心概念,通过直观的代码示例和清晰的逻辑推理,引导读者理解线程与进程在解决并发问题时的不同应用场景。我们将从基础理论出发,逐步过渡到实际案例分析,旨在揭示Python并发模型的内在机制,并比较它们在执行效率、资源占用和适用场景方面的差异。文章不仅适合初学者构建并发编程的基础认识,同时也为有经验的开发者提供深度思考的视角。
|
17天前
|
消息中间件 安全 数据库
动手实操!Python IPC机制,打造高效协同的进程军团
【9月更文挑战第10天】在软件开发领域,进程间的高效协作对应用性能与稳定性至关重要。Python提供了多种进程间通信(IPC)机制,如管道、消息队列、套接字、共享内存等,帮助开发者构建高效协同的系统。本文将通过动手实践,使用`multiprocessing`模块演示如何利用队列实现进程间通信。示例代码展示了如何创建一个工作进程从队列接收并处理数据,从而实现安全高效的进程交互。通过实际操作,读者可以深入了解Python IPC的强大功能,提升系统的并发处理能力。
32 0