subprocess 官方文档简单整理

subprocess

subprocess 模块允许你生成新的进程,连接它们的输入、输出、错误管道,并且获取它们的返回码。此模块打算代替一些老旧的模块与功能:

os.system
os.spawn

run()

运行被 arg 描述的指令, 等待指令完成(阻塞),然后返回一个 CompletedProcess 实例.

这是官方推荐使用的方法。对于更进阶的用例,也可以使用底层的 Popen 接口。

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)

参数:

  • args:默认应该是一个字符串序列,代表命令。如 ['df', '-Th']('df', '-Th') ; 也可以是一个字符串,如 'df -Th',但是此时需要把 shell 参数的值置为 True。通常更推荐使用序列的方式。
  • shell:是否使用 shell 进行执行命令。如果是True,args 参数则使用字符串格式。 同时命令中还支持 shell 管道、文件通配符、环境变量展开以及 ~ 到用户家目录等 shell 功能。在 POSIX,当 shell=True, shell 默认为 /bin/sh
  • capture_output:是否捕获子进程的输出;如果是 True,stdout 和 stdin 将会被捕获从而不显示在标准输出上(stdout, stdin 参数会自动被设为 subprocess.PIPE),因此 stdoutstderr 参数不应当与 capture_output 同时提供。如果希望将两个流合并在一起捕获,使用 stdout=PIPEstderr=STDOUT 的组合来代替 capture_output 参数。
  • stdin, stdout, stderr: 这三个值是用来设置标准输入,标准输出,标准错误的。默认情况下,子进程会继承父进程的设置,会将输出显示在控制台上,当然你也可以将这些参数设置为 PIPE (管道),从而在 CompletedProcess 实例中捕获它们的内容。
  • timeout:设置超时时间;如果超时,子进程将被杀死并等待,在子进程中断后抛出 subprocess.TimeoutExpired
  • input:要传给子进程的数据;input 参数将被传递给 Popen.communicate() 以及子进程的 stdin。 如果使用此参数,它必须是一个字节序列。 如果指定了 encodingerrors 又或者将 text 设置为 True,那么 input 也可以是一个字符串。 当使用此参数时,创建的 Popen 对象会自动带上 stdin=PIPE,并且不能再手动指定 stdin 参数。
  • check:是否检查进程的执行状态;如果 check = True, 并且进程以非零状态码退出, 一个 CalledProcessError 异常将被抛出. 这个异常的属性包含:参数, 退出码, 标准输出和标准错误 等信息。
  • encoding:输入和输出的编码方式;如果 encoding 或者 error 被指定, 又或者 text 被设为 True,标准输入/标准输出和标准错误的文件对象将通过指定的 encodingerrors 以文本模式打开, 否则以默认的 io.TextIOWrapper 打开。
  • text:该参数影响的是输入与输出的数据格式,比如它的值默认为False,此时 stdout 和 stderr 输出的是字节序列;当该参数的值设置为True时,stdout 和 stderr 输出的是字符串。
  • universal_newline:这个参数等同于 text 参数并且提供了向后兼容性. 默认情况下, 文件对象是以二进制模式打开的。
  • cwd:覆盖父进程的当前工作路径,给子进程设置工作目录(shell 为 True 才生效)
  • env:None 或者 字典,给子进程设置环境变量(shell 为 True 才生效)

示例:

>>> subprocess.run(["ls", "-l"])  # 不捕获输出,默认将 output 输出到标准输出,即控制台
crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null
......
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 实例的 stdout 属性中,不会显示到控制台中
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'')

CompletedProcess 对象

run() 函数没有抛出异常,它会返回一个 CompletedProcess 对象,代表一个进程已经结束

对象的属性:

  • args

    进程执行的命令。可能是一个列表或字符串。

  • returncode

    子进程的退出状态码。 通常来说, 一个为 0 的退出码表示进程运行正常。非 0 值代表出现异常,一个负值 -N 表示子进程被信号 N 中断 (仅 POSIX).

  • stdout

    如果运行 run() 时指定了 stdout=subprocess.PIPE ,那么我们可以从子进程捕获到标准输出。如果 run() 设置了 encoding, errors 或者 text=True ,则输出的结果是字符串,否则输出的是字节序列。如果没有捕获输出, 则为 None。如果你通过 stderr=subprocess.STDOUT 运行进程,标准输入和标准错误将被组合在这个属性中,并且 stderr 将为 None

  • stderr

    如果运行 run() 时指定了 stderr=subprocess.PIPE ,我们可以捕获到子进程的标准错误。 如果 run() 是设置了参数 encoding, errors 或者 text=True 运行的,则输出的是字符串,否则输出的是字节序列。如果未有捕获,则为 None.

  • check_returncode()

    如果 returncode 非零, 抛出 CalledProcessError 异常。

Popen()

在一个新的进程中执行子程序,在 POSIX 上,该类会使用类似于 os.execvpe() 的行为来执行子程序。 在 Windows 上,该类会使用 Windows CreateProcess() 函数。默认返回一个 Popen 对象。

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)

参数:和 run() 函数相同的参数,用法是一样的,略过

  • bufsize:管道文件的缓存大小;将在 open() 函数创建了 stdin/stdout/stderr 管道文件对象时作为对应的参数传递给它们:

    • 0 表示不使用缓冲区 (读取与写入是一个系统调用并且可以返回短内容)
    • 1 表示行缓冲(只有 universal_newlines=True 时才有用,例如,在文本模式中)
    • 任何其他正值表示使用一个约为对应大小的缓冲区
    • 负的 bufsize (默认)表示使用系统默认的 io.DEFAULT_BUFFER_SIZE。
  • executable: 一个备用的可执行程序。这个参数很少用到

  • preexec_fn :在子进程执行前先执行的函数。不安全,可能发生死锁。在 3.8 版更改: preexec_fn 形参在子解释器中已不再受支持

  • close_fds :如果设为 True,所有文件描述符除了 0, 1, 2 之外都会在子进程执行前关闭

  • pass_fds:pass_fds 是一个可选的在父子进程间保持打开的文件描述符序列

  • cwd:如果 cwd 不为 None,此函数在执行子进程前会将当前工作目录改为 cwdcwd 可以是一个字符串、字节串或 路径类对象

  • user/group/extra_groups/restore_signals/start_new_session: 这些参数都是 POSIX 上用的,用来设置用户,群组,信号,会话等...

  • creationflags :启动子进程的控制信息,仅在 windows 平台上有效。如:subprocess.CREATE_NEW_CONSOLE可以设置子进程使用新的控制台而非父进程的控制台,有如下 windows 常数供 creationflags 使用:

    • subprocess.CREATE_NEW_PROCESS_GROUP

      用于指明将创建一个新的进程组。 这个旗标对于在子进程上使用 os.kill() 来说是必须的。如果指定了 CREATE_NEW_CONSOLE 则这个旗标会被忽略。

    • subprocess.ABOVE_NORMAL_PRIORITY_CLASS

      用于指明一个新进程将具有高于平均的优先级

    • subprocess.BELOW_NORMAL_PRIORITY_CLASS

      用于指明一个新进程将具有低于平均的优先级

    • subprocess.HIGH_PRIORITY_CLASS

      用于指明一个新进程将具有高优先级

    • subprocess.IDLE_PRIORITY_CLASS

      用于指明一个新进程将具有空闲(最低)优先级

    • subprocess.NORMAL_PRIORITY_CLASS

      用于指明一个新进程将具有正常(默认)优先级

    • subprocess.REALTIME_PRIORITY_CLASS

      用于指明一个新进程将具有实时优先级。 你应当几乎永远不使用这个值,因为这会中断管理鼠标输入、键盘输入以及后台磁盘刷新的系统线程。 这个类只适用于直接与硬件“对话”,或者执行短暂任务具有受限中断的应用。

    • subprocess.CREATE_NO_WINDOW

      指明一个新进程将不会创建窗口

    • subprocess.DETACHED_PROCESS

      指明一个新进程将不会继承其父控制台。 这个值不能与 CREATE_NEW_CONSOLE 一同使用。

    • subprocess.CREATE_DEFAULT_ERROR_MODE

      指明一个新进程不会继承调用方进程的错误模式。 新进程会转为采用默认的错误模式。 这个特性特别适用于运行时禁用硬错误的多线程 shell 应用。

    • subprocess.CREATE_BREAKAWAY_FROM_JOB

      指明一个新进程不会关联到任务的 Popen creationflags 形参

  • startupinfo: 用于设置子进程的一些属性,如:主窗口的外观,进程的优先级等等。参数需要是一个 STARTUPINFO 对象,这个对象可以设置一些标志位,来控制子进程。

Popen 对象

Popen 对象是 Popen() 的返回值。

  • Popen.poll()

    检查子进程是否已被终止。如果已终止则设置并返回 returncode 属性。否则返回 None

  • Popen.wait(timeout=None)

    等待子进程被终止。终止后设置并返回 returncode 属性。如果进程在 timeout 秒后未中断,抛出一个 TimeoutExpired 异常,可以安全地捕获此异常并重新等待(即子进程并没有被杀掉)。

    注意:当 stdout=PIPE 或者 stderr=PIPE 时,进程可能会输出非常多的内容到管道缓冲区,将会阻塞进程,或发生死锁。当使用管道时,可以用 Popen.communicate() 来避免阻塞问题

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

    与进程交互,将数据发送到子进程的 stdin,并从子进程的 stdout 和 stderr 读取数据,直到抵达文件结尾, 等待进程终止并设置进程的 returncode 属性。 可选的 input 参数应为要发送到下级进程的数据,如果没有要发送到下级进程的数据则为 None。 如果流是以文本模式打开的(即创建Popen对象时使用了text=True, encoding 等参数),则 input 必须为字符串。 在其他情况下,它必须为字节串。

    communicate() 返回一个 (stdout_data, stderr_data) 元组。如果文件以文本模式打开则返回值是字符串类型;否则为字节。如果你想要向子进程的 stdin 传输数据,你需要通过 stdin=PIPE 参数创建此 Popen 对象。要从结果元组获取任何非 None 值,你同样需要设置 stdout=PIPE 或者 stderr=PIPE

    如果进程在 timeout 秒后未终止,一个 TimeoutExpired 异常将被抛出。捕获此异常并重新等待将不会丢失任何输出。如果超时到期,子进程不会被杀死,所以为了正确清理一个行为良好的应用程序应该杀死子进程并完成通讯。

proc = subprocess.Popen(...)
try:
    outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
    proc.kill()  # 杀死进程
    outs, errs = proc.communicate()

注解: communicate() 会读取子进程的 stdout 和 stderr 数据到内存里,所以如果数据尺寸过大或无限,不要使用此方法。

  • Popen.send_signal(signal)

    将信号 signal 发送给子进程。如果进程已完成则不做任何操作。

    注解: 在 Windows, SIGTERM 是 terminate() 的别名。 CTRL_C_EVENT 和 CTRL_BREAK_EVENT 可以被发送给以包含 CREATE_NEW_PROCESScreationflags 形参启动的进程。

  • Popen.terminate()

    停止子进程。 在 POSIX 操作系统上,此方法会发送 SIGTERM 给子进程。 在 Windows 上则会调用 Win32 API 函数 TerminateProcess() 来停止子进程。

  • Popen.kill()

    杀死子进程。 在 POSIX 操作系统上,此函数会发送 SIGKILL 给子进程。 在 Windows 上 kill() 则是 terminate() 的别名。

以下属性也是可用的:

  • Popen.args

    创建 Popen 对象时的命令参数-- 一个程序参数的序列或者一个简单字符串。

  • Popen.stdin

    如果 stdin 参数设为 PIPE,此属性就是一个可写的流对象,向此对象写入内容,则内容会作为子进程的输入。如果 encodingerrors 参数被指定或者 universal_newlines 参数为 True,则此流是一个文本流,否则是字节流。如果 stdin 参数非 PIPE, 此属性为 None

  • Popen.stdout

    如果 stdout 参数设为 PIPE,此属性是一个返回的可读流,存放了子进程的标准输出。如果 encodingerrors 参数被指定或者 universal_newlines 参数为 True,此流为文本流,否则为字节流。如果 stdout 参数非 PIPE,此属性为 None

  • Popen.stderr

    如果 stderr 参数是 PIPE,此属性是一个返回的可读流。从流中读取子进程提供的错误输出。如果 encodingerrors 参数被指定或者 universal_newlines 参数为 True,此流为文本流,否则为字节流。如果 stderr 参数非 PIPE,此属性为 None

  • Popen.pid

    子进程的进程号。注意如果你设置了 shell 参数为 True,则这是生成的子 shell 的进程号。

  • Popen.returncode

    此进程的退出码,由 poll()wait() 设置(以及直接由 communicate() 设置)。一个 None 值 表示此进程仍未结束。一个负值 -N 表示子进程被信号 N 中断 (仅 POSIX).

警告

使用 communicate() 而非 .stdin.write.stdout.read 或者 .stderr.read 来避免由于其他 OS 管道缓冲区被占满并且阻塞住子进程导致的死锁。

声明一个 Popen 对象,仅仅是执行了某个进程,不进行后续操作,进程的状态不会改变:

# ================ 示例1
>>> import subprocess as s
>>> p = s.Popen("start C:\software", shell=True)       # 执行完毕后,文件夹就已经被打开了,因此进程应该是结束的状态
>>> p
<Popen: returncode: None args: 'start C:\\software'>   # 但是进程仍未结束
>>> p.communicate()                                    # communicate() 会读取输出,并等待进程结束
(None, None)
>>> p
<Popen: returncode: 0 args: 'start C:\\software'>      # 退出码 0,进程结束了

# ================ 示例2
>>> p = s.Popen("start C:\software", shell=True)
>>> p
<Popen: returncode: None args: 'start C:\\software'>
>>> p.wait()                                          # wait() 也能用来等待进程结束
0
>>> p
<Popen: returncode: 0 args: 'start C:\\software'>


# ================ 示例3
>>> p = s.Popen("start C:\software", shell=True)
>>> p
<Popen: returncode: None args: 'start C:\\software'>
>>> p.poll()                                          # poll() 会判断进程是否结束
0
>>> p
<Popen: returncode: 0 args: 'start C:\\software'>

Popen() 更像是一个设置或者用户打开的命令行窗口(类比用户在命令行窗口执行子进程命令,以及重定向子进程的输入、输出等):

"""
# b.py 文件的内容:
x = input("")
print(f"user inputted: {x}")
"""

import subprocess
from subprocess import PIPE


p = subprocess.Popen("python b.py", shell=True, text=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
print(p.communicate("wzt"))  # ('user inputted: wzt\n', '')

上面代码最重要的是设置 stdin=PIPE ,我们用它来给子进程设置 stdin 的管道,这样我们后续使用 p.communicate() 才能向子进程的输入中发送 wzt 这个数据。

较旧的高阶API

call()

运行由 args 所描述的命令。 等待命令完成,返回 returncode

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

注解

请不要在此函数中使用 stdout=PIPEstderr=PIPE。 如果子进程向管道生成了足以填满 OS 管理缓冲区的输出而管道还未被读取时它将会阻塞。

要想捕获输出,可以使用 run() 方法。

这个函数相当于:run(...).returncode

check_call()

执行并等待命令结束 ,返回退出码 0 或者抛出异常(执行成功返回 0,失败抛出异常)。

如果返回码非零则会引发 CalledProcessError 异常。 CalledProcessError 对象将在 returncode 属性中保存返回码并在 output 属性中保存所有输出。

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

注解

请不要在此函数中使用 stdout=PIPEstderr=PIPE。 如果子进程向管道生成了足以填满 OS 管理缓冲区的输出而管道还未被读取时它将会阻塞。

这个函数相当于:run(..., check=True)

check_output()

执行命令并返回它的输出信息 (也会等待进程执行完毕)。

subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, cwd=None, encoding=None, errors=None, universal_newlines=None, timeout=None, text=None, **other_popen_kwargs)

如果返回码非零则会引发 CalledProcessErrorCalledProcessError 对象将在 returncode 属性中保存返回码并在 output 属性中保存所有输出。

这个函数相当于:run(..., check=True, stdout=PIPE).stdout

getstatusoutput()

返回 (exitcode, output)

getoutput()

返回(stdout 或 stderr)。

CalledProcessError 异常对象

SubprocessError 的子类,当一个被 check_call()check_output() 函数运行的子进程返回了非零退出码时被抛出。

  • returncode

    子进程的退出状态。如果程序由一个信号终止,这将会被设为一个负的信号码。

  • cmd

    用于创建子进程的指令。

  • output

    子进程的输出, 如果被 run()check_output() 捕获。否则为 None

  • stdout

    对 output 的别名,对应的有 stderr

  • stderr

    子进程的标准错误输出,如果被 run() 捕获。 否则为 None

可供输出的对象

在进程中,我们可以将进程的输出定向到下面几中对象上:

subprocess.DEVNULL

可被 Popenstdin, stdout 或者 stderr 参数使用的特殊值, 表示使用特殊文件 os.devnull.

subprocess.PIPE

可被 Popenstdin, stdout 或者 stderr 参数使用的特殊值, 表示打开标准流的管道. 常用于 Popen.communicate()。管道通常用于父子进程之间进行数据交流,可以将其看作一个文件,子进程往里写入,父进程可以从里面读取。

subprocess.STDOUT

可被 Popenstdinstdout 或者 stderr 参数使用的特殊值, 表示标准错误与标准输出使用同一句柄。

名词解释

stdin

标准输入。在常见的 shell 命令中,一个命令通常需要有相应的输入,stdin 代表了标准输入,譬如我们的键盘,如:

$ read    # read 命令可以用来从键盘读取输入
This is input from keyboard    # 这是我输入的内容

stdout, stderr

标准输出和错误。一个 shell 命令会有相应的输出结果或者报错,比如 ls dir1 会输出路径下的文件,如果 dir1 文件夹不存在,则会报错。而默认的输出和错误信息,都是显示在用户终端显示器上的。

PIPE

管道,在 shell 中,我们通常的标准输出和标准错误都是定向到显示器上终端上的,但是我们其实也可以将输出通过'管道',定向到其他地方,譬如传递给其他命令。这样,前一个命令的输出不会显示在显示器的控制台上,而是直接通过管道,作为后续命令的输入内容:

find . -type f | xargs grep name

所以,管道就是用来在进程间传递数据的。它本质是一个文件,设置缓冲区大小,然后一个进程往里面写,一个进程从里面读取,这样就可以传递数据了。因此上面介绍 Popen() 方法时提到可能会有缓冲区满了,导致阻塞的问题。

总结

通篇看下来可能很乱,但其实有用的API就那么几个,下面汇总一下。

run()

官方推荐使用的命令。它会执行命令并等待进程执行完毕,返回一个 CompletedProcess 实例,这个实例包含了进行执行的信息和输出内容。(因此这个方法会阻塞住主进程,因为它要等待子进程执行完毕)。

如果不捕获输出(设置capture_output=Falsestdout=None等),默认会在控制台打印输出。

当开启 check=True,如果命令执行失败,则抛出 subprocess.CalledProcessError 异常。

call()

运行命令。 等待命令完成,返回的是 returncode ,也就是进程的退出状态码,状态码是 0 代表成功,其他数字代表命令执行失败(这个方法也会阻塞主进程)

如果不捕获输出( stdout, stderr 等为 None),默认会在控制台打印输出。

check_call()

执行并等待命令结束。返回 0 或抛出异常(如果返回码非零则会引发 CalledProcessError 异常)

如果不捕获输出( stdout, stderr 等为 None),默认会在控制台打印输出。

check_output()

执行命令并返回其输出信息 ,命令执行失败会抛出异常。

默认不会在控制台打印输出。

getstatusoutput()

返回命令产生的 (exitcode, output) 元组。

默认不会在控制台打印输出。

getoutput()

返回命令产生的输出信息或错误(stdout 或 stderr)

默认不会在控制台打印输出。

Popen()

默认返回一个 Popen 对象,不会等待进程执行完毕,不会阻塞主进程。

返回的 Popen 对象,这个进程需要手动杀死,如p.kill() 等。

但是当执行 Popen 对象的方法: p.communicate() 时,如果没有设置超时时间 timeout,则 communicate() 这个方法会阻塞主进程,因为它会等待进程结束,然后返回一个 (stdout_data, stderr_data) 元组。如果设置了超时时间,一旦超时则会返回一个异常错误TimeoutExpired,但是子进程并没有被杀死

如果不捕获输出( stdout, stderr 等为 None),执行 Popen.communicaate 默认会在控制台打印输出, 并且返回值是 (None, None)。

什么时候指定 stdout, stderr

一、想要捕获子进程的标准输出、标准错误时

想要捕获子进程的标准输出、标准错误时,可以设置 stdout=subprocess.PIPE, stderr=subprocess.PIPE。当我们执行命令如:

p = subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE)
p.stdout.readline()  # 会被捕获到 p.stdout 中

意思就是:在子进程中运行 dir ,并且将子进程的 stdout 设置为 subprocess.PIPE ,因此子进程的输出就会被重定向到管道中,不会显示在父进程的标准输出中(即不会显示在控制台)。

管道的数据是缓冲的,所以如果数据尺寸过大,则子进程可能会向管道生成足以填满缓冲区的输出,而如果管道未被读取,则它将会阻塞。因此,针对 subprocess.call(), subprocess.check_call() 不建议设置 stdout=subprocess.PIPE, 可以使用 Popen.communicate() 来避免这个问题,因为 communicate() 可以读取管道中的数据直到进程结束。

二、想要屏蔽子进程的标准输出、错误时

如果不想要将子进程的输出显示到父进程中,可以设置它:

p = subprocess.Popen("dir", shell=True, stdout=subprocess.DEVNULL)  # 输出到 DEVNULL 中

附录

STARTUPINFO 对象(不常用)

这个类只能用于 windows 平台,用来控制子进程的窗口和输入输出。它的实例可以作为参数传递给 Popen(startupinfo=...) 来控制子进程的输入输出,是否显示窗口等信息。

subprocess.STARTUPINFO(*, dwFlags=0, hStdInput=None, hStdOutput=None, hStdError=None, wShowWindow=0, lpAttributeList=None)
  • dwFlags

    一个位字段(bit field) 来决定子进程创建窗口时的属性:

si = subprocess.STARTUPINFO()
si.dwFlags = subprocess.STARTF_USESTDHANDLES | subprocess.STARTF_USESHOWWINDOW

"""有如下值可以设置:
- `subprocess.STD_INPUT_HANDLE`
  标准输入设备,控制台输入缓冲区 `CONIN$`。

- `subprocess.STD_OUTPUT_HANDLE`
  标准输出设备。活动控制台屏幕缓冲区 `CONOUT$`。

- `subprocess.STD_ERROR_HANDLE`
  标准错误设备。活动控制台屏幕缓冲区 `CONOUT$`。

- `subprocess.SW_HIDE`
  隐藏窗口。另一个窗口将被激活。

- `subprocess.STARTF_USESTDHANDLES`
  给 StartUpInfo 类使用的常量,表示使用标准句柄处理输入和输出。指明这个类的 `STARTUPINFO.hStdInput`, `STARTUPINFO.hStdOutput` 和 `STARTUPINFO.hStdError` 属性包含额外的信息。

- `subprocess.STARTF_USESHOWWINDOW`
  指明 `STARTUPINFO.wShowWindow`属性包含额外的信息。表示显示窗口。

- `subprocess.CREATE_NEW_CONSOLE`
  新的进程将有新的控制台,而不是继承父进程的(默认)控制台。
"""
  • hStdInput

    如果 dwFlags 指定了 STARTF_USESTDHANDLES, 这个属性就是进程的标准输入句柄. 如果 STARTF_USESTDHANDLES 没被指定,那么标准输入就是键盘缓冲区。

  • hStdOutput

    如果 dwFlags 指定了 STARTF_USESTDHANDLES,这个属性代表了进程的标准输出处理方式;否则这个参数会被忽略并且标准输出会变成控制台窗口的缓冲区。

  • hStdError

    如果 dwFlags 指定了 STARTF_USESTDHANDLES,这个属性代表了进程的标准错误处理方式;否则这个参数会被忽略并且标准输出会变成控制台窗口的缓冲区。

  • wShowWindow

    如果 dwFlags 指定了 STARTF_USESTDHANDLES,除了 SW_SHOWDEFAULT意外,这个属性可以是 ShowWindow 函数中任何合法的 nCmdShow 值(即一个数字,控制窗口如何显示)。否则这个参数会被忽略。

    subprocess.SW_HIDE 这个常量可以被提供给这个属性,只有当 shell=True 时才会有用。

  • lpAttributeList

    一个包含了额外属性的字典,可以用来给子进程设置属性。

创建一个全新的独立进程(非子进程)

windows 平台

import subprocess

subprocess.Popen([sys.executable, __file__],  creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP)  # 不使用父进程的控制台,并且在新的进程组中创建进程

POSIX 平台(没试过好不好使):

import subprocess

p = subprocess.Popen(["sleep", "30"], start_new_session=True)  # 开启新的会话

# 又或者:
# subprocess.Popen(['nohup', 'my_command'],
#                  stdout=open('/dev/null', 'w'),
#                  preexec_fn=os.setpgrp                # 执行进程前,先设置一个新的进程组
#                  )
posted @ 2022-04-01 23:23  wztshine  阅读(562)  评论(0编辑  收藏  举报