简介

subprocess 是 Python 中执行操作系统级别的命令的模块,所谓系级级别的命令就是如ls /etc/user ifconfig 等和操作系统有关的命令。

subprocess 创建子进程来执行相关命令,并连接它们的输入、输出和错误管道,获取它们的返回状态。

subprocess 来源

Subprocess模块开发之前,标准库已有大量用于执行系统级别命令的的方法,如os.system、os.spawn等。但是略显混乱使开发者难以抉择,因此subprocess的目的是打造一个统一模块来替换之前执行系统界别命令的方法。

所以 推荐使用subprocess替代了一些老的方法,比如:os.system、os.spawn*等。

模块常用函数

函数清单总览

Subprocess 模块推荐使用run方法替换低版本方法,如果想要更加精细的控制可以使用Popen方法。所以本教程中重点介绍run和Popen方法。

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
)

简单使用

执行简单shell命令

默认情况下,子进程会继承父进程的设置,会将输出显示在终端上

import subprocess

res = subprocess.run("ls -al /home/ljk/Videos", shell=True)
>>>
(ymir) ➜ subprocess_demo python3 subprocess_demo.py
总用量 96
drwxr-xr-x 3 ljk ljk 4096 4月 11 11:04 .
drwxr-x--- 62 ljk ljk 4096 7月 6 13:40 ..
-rw-r--r-- 1 ljk ljk 84176 4月 11 11:04 346e30f4-9119-11eb-bb4a-4a238cf0c417.mp4
lrwxrwxrwx 1 ljk ljk 36 10月 21 2022 dde-introduction.mp4 -> /usr/share/dde-introduction/demo.mp4
drwxr-xr-x 2 ljk ljk 4096 4月 11 18:26 'Screen Recordings'

如果命令没有输出则不会打印输出信息

获取状态码

returncode 是subprocess的返回码

import subprocess

res = subprocess.run("ls -al /home/ljk/Videos", shell=True)

print("returncode:", res.returncode)
>>>
总用量 96
drwxr-xr-x 3 ljk ljk 4096 7月 6 13:41 .
drwxr-x--- 62 ljk ljk 4096 7月 6 13:47 ..
-rw-r--r-- 1 ljk ljk 84176 4月 11 11:04 346e30f4-9119-11eb-bb4a-4a238cf0c417.mp4
-rw-r--r-- 1 ljk ljk 0 7月 6 13:41 a.txt
lrwxrwxrwx 1 ljk ljk 36 10月 21 2022 dde-introduction.mp4 -> /usr/share/dde-introduction/demo.mp4
drwxr-xr-x 2 ljk ljk 4096 4月 11 18:26 'Screen Recordings'
0

参数介绍

args:要执行的命令,可以是字符串形式或由命令及其参数组成的列表。例如,['ls', '-l'] 或 'ls -l'。

input:允许将字节或字符串传递给子进程的标准输入(stdin)。

stdin:子进程的标准输入。默认为None,可以是以下三个参数:

  1. subprocess.PIPE 创建一个管道,允许与子进程进行通信
  2. subprocess.DEVNULL 特殊的文件对象,可以将其用于丢弃子进程的输出
  3. 一个打开的文件对象,将内容写入文件

stdout: 同 stdin

stderr: 同 stdin

capture_output :这个参数控制是否捕获外部命令的标准输出(stdout)和标准错误(stderr)。如果将其设置为True,run()函数将返回一个CompletedProcess对象,该对象具有stdout和stderr属性,分别存储了命令的标准输出和标准错误输出。如果设置为False,标准输出和标准错误将被发送到控制台。默认为False。

shell:指定是否通过shell来执行命令。如果为True,命令将在shell中执行;如果为False,则直接调用可执行文件。默认为False。

cwd:设置子进程的工作目录。默认为None,表示使用当前工作目录。

timeout:设置子进程的超时时间(秒)。如果子进程在指定的时间内没有运行完成,则会引发TimeoutExpired异常。

check:设置是否检查子进程的返回码。如果为True,并且子进程的返回码不为零,则会引发CalledProcessError异常。

encoding:该参数指定输出结果的字符编码。默认情况下,它是None,表示使用原始的字节数据。如果提供了有效的编码名称(如"utf-8"、"gbk"等),run()函数将自动将输出解码为字符串。

errors:该参数定义在解码输出时如何处理编码错误。它与Python的str.decode()函数的相同参数含义相匹配。常用的值包括"strict" (默认值,抛出异常)、"ignore" (忽略错误字符) 和 "replace" (用替代字符代替错误字符)。

text:指定是否将输出结果以文本形式返回。如果为True,则结果以字符串形式返回,同时input或者stdin参数也需要输入String;如果为False,则返回字节流。默认为False。

env:该参数允许您为子进程指定环境变量。它可以接受一个字典类型的对象,其中键是环境变量的名称,值是环境变量的值。通过设置env参数,您可以在子进程中使用特定的环境变量。

universal_newlines: 该参数影响的是输入与输出的数据格式,比如它的值默认为False,此时stdout和stderr的输出是字节序列;当该参数的值设置为True时,stdout和stderr的输出是字符串。

args 执行命令

args传入的是要执行的系统命令,可以接收两种方法:字符串或列表。

  • 使用列表形式subprocess.run(["ls", "-al"])
  • 使用字符串形式 subprocess.run("ls -al", shell=True)。使用字符串形式必须设置参数shell=True
import subprocess

subprocess.run(["ls", "-al", "/Users/ljk/Documents/code/daily_dev"])

subprocess.run("ls -al /Users/ljk/Documents/code/daily_dev", shell=True)

>>>
➜ subprocess_demo python3 subprocess_demo.py
total 0
drwxr-xr-x 5 ljk staff 160 7 11 22:27 .
drwxr-xr-x@ 18 ljk staff 576 7 3 22:11 ..
drwxr-xr-x 3 ljk staff 96 6 24 18:28 docker_dev
drwxr-xr-x 3 ljk staff 96 6 17 22:08 requests_demo
drwxr-xr-x 4 ljk staff 128 7 11 22:30 subprocess_demo
total 0
drwxr-xr-x 5 ljk staff 160 7 11 22:27 .
drwxr-xr-x@ 18 ljk staff 576 7 3 22:11 ..
drwxr-xr-x 3 ljk staff 96 6 24 18:28 docker_dev
drwxr-xr-x 3 ljk staff 96 6 17 22:08 requests_demo
drwxr-xr-x 4 ljk staff 128 7 11 22:30 subprocess_demo

默认情况下,命令的输出是直接打印到控制台上的。

stdin、stdout、sterr 设置命令输出输入的对象

这三个值是用来设置标准输入,标准输出,标准错误的。默认情况下,子进程会继承父进程的设置,会将输出显示在控制台上,除此之外也可以设置成如下三个值:

  1. subprocess.PIPE 创建一个管道,允许与子进程进行通信
  2. subprocess.DEVNULL 特殊的文件对象,可以将其用于丢弃子进程的输出
  3. 一个打开的文件对象,将内容写入文件

    以studout为例子,验证这三个输出选项。

将命令输出保存到管道

import subprocess

res = subprocess.run(["ls", "-al", "/Users/ljk/Documents/code/daily_dev"], stdout=subprocess.PIPE)

print(res.returncode)
print(res.stdout)
>>>
0
b'total 0\ndrwxr-xr-x 5 ljk staff 160 7 11 22:27 .\ndrwxr-xr-x@ 18 ljk staff 576 7 3 22:11 ..\ndrwxr-xr-x 3 ljk staff 96 6 24 18:28 docker_dev\ndrwxr-xr-x 3 ljk staff 96 6 17 22:08 requests_demo\ndrwxr-xr-x 4 ljk staff 128 7 11 22:44 subprocess_demo\n'

命令输出不再打印到控制台上,而是保存到对象里,通过对象的stdout获取到。此时命令输出结果是字节串格式的。可以通过设置text=True,将命令输出以文本形式保存。

import subprocess

res = subprocess.run(["ls", "-al", "/Users/ljk/Documents/code/daily_dev"], stdout=subprocess.PIPE, text=True)

print(res.returncode)
print(res.stdout)
>>>
0
total 0
drwxr-xr-x 5 ljk staff 160 7 11 22:27 .
drwxr-xr-x@ 18 ljk staff 576 7 3 22:11 ..
drwxr-xr-x 3 ljk staff 96 6 24 18:28 docker_dev
drwxr-xr-x 3 ljk staff 96 6 17 22:08 requests_demo
drwxr-xr-x 4 ljk staff 128 7 11 22:48 subprocess_demo

命令输出保存到文件中

可以将命令的输出保存到一个文件中,stdout传入一个打开的文件对象即可。

import subprocess

with open("a.txt", "a+") as f:
res = subprocess.run(["ls", "-al", "/Users/ljk/Documents/code/daily_dev"], stdout=f, text=True)
print(res.returncode)
print(res.stdout) >>>
0
None

此时在目录下生成了a.txt文件,里面保存的是命令的输出结果

capture_output 捕获控制台输出

捕获命令输出。默认为false,所有的命令输出都打印到控制台。设置为true,所有命令的输出都被捕获保存到返回对象中。

import subprocess

res = subprocess.run("ls -al /Users/ljk/Documents/code/daily_dev", shell=True, capture_output=True)
print(res.returncode)
print(res.stdout)
>>>
0
b'total 0\ndrwxr-xr-x 5 ljk staff 160 7 11 22:27 .\ndrwxr-xr-x@ 18 ljk staff 576 7 3 22:11 ..\ndrwxr-xr-x 3 ljk staff 96 6 24 18:28 docker_dev\ndrwxr-xr-x 3 ljk staff 96 6 17 22:08 requests_demo\ndrwxr-xr-x 5 ljk staff 160 7 13 20:38 subprocess_demo\n'

cwd 设置命令执行的目录

设置子进程的工作目录。默认为None,表示使用当前工作目录

import subprocess

res = subprocess.run("pwd", shell=True, cwd="/Users/ljk/Documents/code/")
print(res.returncode) /Users/ljk/Documents/code
0

如果脚本需要在特定的目录中执行,可以设置该参数

timeout 设置命令超执行时时间

当一些命令有时间上的要求,可以设置命令执行的超时时间。如果命令在指定的时间内没有运行完成,则会引发TimeoutExpired异常。

import subprocess

res = subprocess.run("sleep 10 && ls", shell=True, timeout=5)
print(res.returncode)
>>>
Traceback (most recent call last):
File "/Users/ljk/Documents/code/daily_dev/subprocess_demo/subprocess_demo.py", line 22, in <module>
res = subprocess.run("sleep 10 && ls", shell=True, timeout=5)
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/subprocess.py", line 507, in run
stdout, stderr = process.communicate(input, timeout=timeout)
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/subprocess.py", line 1134, in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/subprocess.py", line 2005, in _communicate
self.wait(timeout=self._remaining_time(endtime))
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/subprocess.py", line 1189, in wait
return self._wait(timeout=timeout)
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/subprocess.py", line 1909, in _wait
raise TimeoutExpired(self.args, timeout)
subprocess.TimeoutExpired: Command 'sleep 10 && ls' timed out after 4.999915208999999 seconds

执行的命令是先睡眠10s,然后执行ls。设置的超时时间是5秒,所以执行的第5s就抛出timeout错误。

check 返回码非1抛出错误

检查子进程的返回码。如果为True,并且子进程的返回码不为零,则会引发CalledProcessError异常。以下代码做了一组对比,ls一个不存在目录,设置check=True的会抛出异常。

res = subprocess.run("ls no_exsit.txt", shell=True)
print(res.returncode)
>>>
ls: no_exsit.txt: No such file or directory
1 res = subprocess.run("ls no_exsit.txt", shell=True, check=True)
print(res.returncode)
>>>
ls: no_exsit.txt: No such file or directory
Traceback (most recent call last):
File "/Users/ljk/Documents/code/daily_dev/subprocess_demo/subprocess_demo.py", line 25, in <module>
res = subprocess.run("ls no_exsit.txt", shell=True, check=True)
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/subprocess.py", line 528, in run
raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command 'ls no_exsit.txt' returned non-zero exit status 1.

encoding、text 设置输出结果的格式

encoding 用于设置命令输出的编码格式。 默认情况下,它是None,表示使用原始的字节数据。如果提供了有效的编码名称,如"utf-8"、"gbk",将自动将输出解码为字符串。示例演示encoding=True

# 原始输出
res = subprocess.run("ls -al /Users/ljk/Documents/code/daily_dev", shell=True, capture_output=True)
print(res.returncode)
print(res.stdout)
>>>
0
b'total 0\ndrwxr-xr-x 5 ljk staff 160 7 11 22:27 .\ndrwxr-xr-x@ 18 ljk staff 576 7 3 22:11 ..\ndrwxr-xr-x 3 ljk staff 96 6 24 18:28 docker_dev\ndrwxr-xr-x 3 ljk staff 96 6 17 22:08 requests_demo\ndrwxr-xr-x 5 ljk staff 160 7 13 21:07 subprocess_demo\n' # 设置encoding="utf-8"
res = subprocess.run("ls -al /Users/ljk/Documents/code/daily_dev", shell=True, capture_output=True, encoding="utf-8")
print(res.returncode)
print(res.stdout) >>>
0
total 0
drwxr-xr-x 5 ljk staff 160 7 11 22:27 .
drwxr-xr-x@ 18 ljk staff 576 7 3 22:11 ..
drwxr-xr-x 3 ljk staff 96 6 24 18:28 docker_dev
drwxr-xr-x 3 ljk staff 96 6 17 22:08 requests_demo
drwxr-xr-x 5 ljk staff 160 7 13 21:07 subprocess_demo

text 参数是用于设置命令输出的格式。命令输出默认是字节串,text=True表示输出格式为字符串。和encoding=True 基本等价。

# 原始输出
res = subprocess.run("ls -al /Users/ljk/Documents/code/daily_dev", shell=True, capture_output=True)
print(res.returncode)
print(res.stdout)
>>>
0
b'total 0\ndrwxr-xr-x 5 ljk staff 160 7 11 22:27 .\ndrwxr-xr-x@ 18 ljk staff 576 7 3 22:11 ..\ndrwxr-xr-x 3 ljk staff 96 6 24 18:28 docker_dev\ndrwxr-xr-x 3 ljk staff 96 6 17 22:08 requests_demo\ndrwxr-xr-x 5 ljk staff 160 7 13 21:10 subprocess_demo\n' # 设置text=True
res = subprocess.run("ls -al /Users/ljk/Documents/code/daily_dev", shell=True, capture_output=True, text=True)
print(res.returncode)
print(res.stdout)
>>>
0
total 0
drwxr-xr-x 5 ljk staff 160 7 11 22:27 .
drwxr-xr-x@ 18 ljk staff 576 7 3 22:11 ..
drwxr-xr-x 3 ljk staff 96 6 24 18:28 docker_dev
drwxr-xr-x 3 ljk staff 96 6 17 22:08 requests_demo
drwxr-xr-x 5 ljk staff 160 7 13 21:10 subprocess_demo

返回对象

subprocess.run()函数返回值是一个CompletedProcess类的实例,subprocess.completedPorcess类是Python 3.5以上才存在的。它表示的是一个已结束进程的状态信息,它所包含的属性和方法如下:

args: 用于加载该进程的参数,这可能是一个列表或一个字符串。

returncode: 子进程的退出状态码。通常情况下,退出状态码为0则表示进程成功运行了;一个负值-N表示这个子进程被信号N终止了。

stdout: 从子进程捕获的stdout。这通常是一个字节串。如果设置了encoding或text参数,返回就是字符串。

stderr: 从子进程捕获的stderr。它的值与stdout一样,是一个字节序列或一个字符串。如果stderr没有被捕获的话,它的值就为None。

check_returncode(): 如果returncode是一个非0值,则该方法会抛出一个CalledProcessError异常。

示例:

import subprocess

res = subprocess.run("ls -al /home/ljk/Videos", shell=True)

print("args:", res.args)
print("returncode:", res.returncode)
print("stdout:", res.stdout)
print("stderr:", res.stderr)
print("returncode():", res.check_returncode()) >>>
➜ subprocess_demo python3 subprocess_demo.py
总用量 96
drwxr-xr-x 3 ljk ljk 4096 7月 6 13:41 .
drwxr-x--- 62 ljk ljk 4096 7月 6 13:47 ..
-rw-r--r-- 1 ljk ljk 84176 4月 11 11:04 346e30f4-9119-11eb-bb4a-4a238cf0c417.mp4
-rw-r--r-- 1 ljk ljk 0 7月 6 13:41 a.txt
lrwxrwxrwx 1 ljk ljk 36 10月 21 2022 dde-introduction.mp4 -> /usr/share/dde-introduction/demo.mp4
drwxr-xr-x 2 ljk ljk 4096 4月 11 18:26 'Screen Recordings'
args: ls -al /home/ljk/Videos
returncode: 0
stdout: None
stderr: None
returncode(): None

subprocess.Popen()

popen是一个功能更强大的方法,而run是它的一个简化版。如果run函数不能满足功能的要求,可以尝试功能更多的popen方法。

除了方法的多少之外,run和popen最大的区别在于:run方法是阻塞调用,会一直等待命令执行完成或失败;popen是非阻塞调用,执行之后立刻返回,结果通过返回对象获取。

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,
process_group=None
)

简单使用

和run一样执行命令

import subprocess

subprocess.Popen("ls -al /Users/ljk/Documents/code/daily_dev", shell=True)

>>>>
None
total 0
drwxr-xr-x 5 ljk staff 160 7 11 22:27 .
drwxr-xr-x@ 18 ljk staff 576 7 3 22:11 ..
drwxr-xr-x 3 ljk staff 96 6 24 18:28 docker_dev
drwxr-xr-x 3 ljk staff 96 6 17 22:08 requests_demo
drwxr-xr-x 5 ljk staff 160 7 13 21:32 subprocess_demo

执行阻塞命令

import subprocess

res = subprocess.Popen("sleep 10 && ls -al", shell=True)
print(res)
>>>
<Popen: returncode: None args: 'sleep 10 && ls -al'>

遇到阻塞命令也会直接返回,返回是一个对象。可以通过对象获取命令执行的结果。

参数介绍

注意:因为run是popen的一个简化版本,所以run拥有的函数popen也拥有。这里就不再重复说明了。

bufsize:定义了子进程的缓冲大小。可选参数,默认为-1,表示使用系统默认的缓冲大小。

executable:指定要执行的程序路径。如果未提供该值,则通过PATH环境变量来确定可执行文件的位置。

preexec_fn:指定在子进程启动之前将要执行的函数。该函数将在fork()调用成功,但exec()调用之前被调用。

close_fds:指定是否关闭所有文件描述符。默认为False。

start_new_session(仅 POSIX):如果该参数设置为True,则在启动子进程时创建一个新的进程会话。默认为False。

pass_fds(仅 POSIX):通过这个参数传递一个文件描述符集合,这些文件描述符将保持打开状态并传递给子进程。默认为None。

startupinfo:一个可选的subprocess.STARTUPINFO对象,用于指定子进程的启动信息,如窗口大小、窗口标题等。默认为None。

creationflags:用于指定子进程的创建标志,控制子进程的各种行为。可以使用subprocess.CREATE_NEW_CONSOLE、subprocess.CREATE_NEW_PROCESS_GROUP等常量进行设置。默认为0。

restore_signals(仅 POSIX):用于确定是否在子进程中恢复信号处理程序的默认行为。默认为True。

group(仅 POSIX): 如果 group 不为 None,则 setregid() 系统调用将于子进程执行之前在下级进程中进行。 如果所提供的值为一个字符串,将通过 grp.getgrnam() 来查找它,并将使用 gr_gid 中的值。 如果该值为一个整数,它将被原样传递。 (POSIX 专属)

extra_groups(仅 POSIX): 如果 extra_groups 不为 None,则 setgroups() 系统调用将于子进程之前在下级进程中进行。 在 extra_groups 中提供的字符串将通过 grp.getgrnam() 来查找,并将使用 gr_gid 中的值。 整数值将被原样传递。

user(仅 POSIX): 如果 user 不为 None,则 setreuid() 系统调用将于子进程执行之前在下级进程中进行。 如果所提供的值为一个字符串,将通过 pwd.getpwnam() 来查找它,并将使用 pw_uid 中的值。 如果该值为一个整数,它将被原样传递。 (POSIX 专属)

Popen类的方法与参数介绍

communicate(input=None, timeout=None): 与子进程进行交互,发送输入并获取输出结果。可以在参数input中指定要发送给子进程的输入内容。该方法会阻塞当前进程,直到子进程完成并返回输出结果。可选的timeout参数用于设置超时时间。

poll(): 检查子进程是否已经退出,如果已退出则返回退出状态码,否则返回None。

wait(timeout=None): 等待子进程完成并返回退出状态码。可选的timeout参数用于设置超时时间。

terminate(): 向子进程发送终止信号。这通常是优雅地终止子进程。

kill(): 强制终止子进程。

send_signal(signal): 向子进程发送信号,其中signal参数表示要发送的信号类型,如SIGINT、SIGTERM等。

communicate 获取命令输出

发送输入并获取输出结果。可以在参数input中指定要发送给子进程的输入内容。该方法会阻塞当前进程,直到子进程完成并返回输出结果。函数返回一个元组: (stdoutdata , stderrdata )

import subprocess

res = subprocess.Popen("sleep 3 && ls -al", shell=True)
print(res.communicate())
>>>
total 24
drwxr-xr-x 5 ljk staff 160 7 13 21:40 .
drwxr-xr-x 5 ljk staff 160 7 11 22:27 ..
-rw-r--r-- 1 ljk staff 269 7 11 22:50 a.txt
-rwxrwxrwx 1 ljk staff 42 7 11 22:29 ls_demo.sh
-rw-r--r-- 1 ljk staff 1069 7 13 21:40 subprocess_demo.py
(None, None)

该方法和run函数行为一致,将非阻塞调用变成阻塞调用。

subprocess.Popen().communicate() 等价于 subprocess.run()

poll 检查子进程

Poll 检查子进程是否已经退出,如果已退出则返回退出状态码,否则返回None。

import time

res = subprocess.Popen("sleep 3 && ls -al", shell=True)
print(res.poll())
time.sleep(4)
print(res.poll())
>>>> None
total 24
drwxr-xr-x 5 ljk staff 160 7 13 21:39 .
drwxr-xr-x 5 ljk staff 160 7 11 22:27 ..
-rw-r--r-- 1 ljk staff 269 7 11 22:50 a.txt
-rwxrwxrwx 1 ljk staff 42 7 11 22:29 ls_demo.sh
-rw-r--r-- 1 ljk staff 1097 7 13 21:39 subprocess_demo.py
0

在执行命令之后立刻用poll检查发现返回None,此时因为子进程还没有退出。程序睡眠4s之后命令已经退出,再次执行poll返回了状态码。在两次打印中间是命令的标准输出。

检查命令执行状态并获取返回值

res = subprocess.Popen("sleep 3 && ls -al", shell=True, stdout=subprocess.PIPE, encoding="utf-8")
while res.poll() is None:
time.sleep(0.5)
print("命令还在执行中...")
print("命令执行完成,获取结果:")
print(res.communicate())

wait 用于等待命令执行完成

等待子进程完成并返回退出状态码。可选的timeout参数用于设置超时时间。

# 等待,不设置超时
import subprocess res = subprocess.Popen("sleep 3 && ls -al", shell=True)
print(res.wait()) >>>
total 24
drwxr-xr-x 5 ljk staff 160 7 13 22:04 .
drwxr-xr-x 5 ljk staff 160 7 11 22:27 ..
-rw-r--r-- 1 ljk staff 269 7 11 22:50 a.txt
-rwxrwxrwx 1 ljk staff 42 7 11 22:29 ls_demo.sh
-rw-r--r-- 1 ljk staff 1071 7 13 22:04 subprocess_demo.py
0 # 等待,设置超时,报错
res = subprocess.Popen("sleep 6 && ls -al", shell=True)
print(res.wait(timeout=5)) >>>
Traceback (most recent call last):
File "/Users/ljk/Documents/code/daily_dev/subprocess_demo/subprocess_demo.py", line 41, in <module>
print(res.wait(timeout=5))
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/subprocess.py", line 1189, in wait
return self._wait(timeout=timeout)
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/subprocess.py", line 1909, in _wait
raise TimeoutExpired(self.args, timeout)
subprocess.TimeoutExpired: Command 'sleep 6 && ls -al' timed out after 5 seconds drwxr-xr-x 5 ljk staff 160 7 13 22:04 .
drwxr-xr-x 5 ljk staff 160 7 11 22:27 ..
-rw-r--r-- 1 ljk staff 269 7 11 22:50 a.txt
-rwxrwxrwx 1 ljk staff 42 7 11 22:29 ls_demo.sh
-rw-r--r-- 1 ljk staff 1071 7 13 22:04 subprocess_demo.py

wait 设置了超时,在指定时间之内没有执行完成会抛出异常,但是命令还是会在后端继续执行,没有停止

terminate 优雅的终止执行的命令

Terminate 可以终止一个还没有执行完成的命令。wait设置超时之后虽然会抛出异常,但是并不会终止命令。而terminate就可以优雅的终止命令。

import subprocess

res = subprocess.Popen("sleep 3 && ls -al", shell=True)
print(res.poll())
res.terminate()
>>>
None

如果没有终止会打印输出信息,而终止之后就不会再打印出来了。所谓优雅可能是停止命令之前会关闭打开的文件,管道,套接字等。

kill 强制终止执行的命令

使用kill可以强制将执行的命令杀死,类似于linux系统中的kill命令。kill不会关闭已经打开的文件句柄等。

import subprocess

res = subprocess.Popen("sleep 3 && ls -al", shell=True)
print(res.poll())
res.kill()

异常捕获

subprocess 会抛出一些异常,自带的异常捕获模块可以完成相关异常的捕获

TimeoutExpired

异常类型:class subprocess.TimeoutExpired(cmd, timeout, output=None)

当子进程执行时间超过指定的超时时间时引发。

属性:

  • cmd:执行的命令。
  • timeout:设定的超时时间。
  • output:子进程输出的内容。

CalledProcessError

异常类型:class subprocess.CalledProcessError(returncode, cmd, output=None, stderr=None)

在使用 check_output() 或 check_call() 函数执行外部命令并返回非零退出码时引发。

属性:

  • returncode:子进程的返回码。
  • cmd:已执行的命令。
  • output:标准输出的内容(如果没有重定向则为 None)
  • stderr:标准错误的内容(如果没有重定向则为 None)

使用示例:

import subprocess

try:
res = subprocess.run("ls no_exsit.txt", shell=True, check=True)
except subprocess.CalledProcessError as e:
print("returncode:", e.returncode)
print("cmd:", e.cmd)
print("output:", e.output)
print("stderr:", e.stderr >>>
ls: 无法访问'no_exsit.txt': 没有那个文件或目录
returncode: 2
cmd: ls no_exsit.txt
output: None
stderr: None

subprocess.SubprocessError

这是其他subprocess常类的基类,可以用于捕获所有与子进程相关的异常。

旧函数简介

  1. subprocess.call()

    函数执行给定的命令,并等待其完成。它返回命令的退出码。

    示例代码:
import subprocess

return_code = subprocess.call(["ls", "-l"])
print(f"Command returned with exit code: {return_code}")
  1. subprocess.check_call()

    check_call() 函数也执行给定的命令,但与 call() 不同的是,如果命令返回非零的退出码,则会引发 CalledProcessError 异常。

示例代码:

import subprocess

subprocess.check_call(["ls", "-l"])
print("Command executed successfully")
  1. subprocess.getoutput()

    getoutput() 函数执行给定的命令,并返回其输出作为字符串。

    示例代码:
import subprocess

output = subprocess.getoutput("echo Hello, subprocess!")
print(output)
  1. subprocess.getstatusoutput()

    getstatusoutput() 函数执行给定的命令,并返回一个元组,包含命令的退出状态码和输出结果的字符串。

    示例代码:
import subprocess

status, output = subprocess.getstatusoutput("ls -l")
print(f"Exit status: {status}")
print(f"Output: {output}")
  1. subprocess.check_output()

    check_output() 函数执行给定的命令,并返回其输出结果作为字节字符串。

    示例代码:
import subprocess

output = subprocess.check_output(["ls", "-l"])
print(output.decode("utf-8"))

subprocess 和 os 模块比较

与os模块对比而言,subprocess模块具有以下这些优势:

  1. 更丰富的功能

    subprocess模块提供了更多的方法和选项来执行子进程,并与其进行交互。例如,可以捕获子进程的输出、发送输入数据、设置超时时间等。
  2. 更强的灵活性

    subprocess模块允许您以多种不同的方式执行子进程,包括使用管道、重定向输入输出、执行shell命令等。这使得您能够更灵活地控制和处理子进程的输入输出。
  3. 更好的安全性

    subprocess模块提供了更严格的参数处理机制,可以帮助避免常见的安全问题,如命令注入攻击。它支持传递参数列表而不是字符串,从而减少了潜在的安全漏洞。

因此,当执行复杂的子进程操作、需要更多控制权和灵活性、以及考虑到安全性时,优先选择subprocess模块是更好的选择。而对于简单的命令执行需求或与操作系统相关的功能,os模块可能更加适合。

subprocess Python执行系统命令最优选模块的更多相关文章

  1. python 执行系统命令模块比较

    python 执行系统命令模块比较 1.os.system模块 仅仅在子终端运行命令,返回状态码,0为成功,其他为失败,但是不返回执行结果 如果再命令行下执行,结果直接打印出来 >>> ...

  2. 转 Python执行系统命令的方法

    传送门 Python执行系统命令的方法 http://www.linux-field.com/?p=15 Python中执行系统命令常见方法有两种: 两者均需 import os (1) os.sys ...

  3. windows linux 使用python执行系统命令并将结果保存到变量

    最近需要用到os.system 发现不能赋值到变量 后查有更新的模块,如下: os.system os.spawn* os.popen* popen2.* commands.* 重新使用content ...

  4. python执行系统命令后获取返回值的几种方式集合

    python执行系统命令后获取返回值的几种方式集合 今天小编就为大家分享一篇python执行系统命令后获取返回值的几种方式集合,具有很好的参考价值,希望对大家有所帮助.一起跟随小编过来看看吧 第一种情 ...

  5. Python执行系统命令的方法 os.system(),os.popen(),commands

    os.popen():用python执行shell的命令,并且返回了结果,括号中是写shell命令 Python执行系统命令的方法: https://my.oschina.net/renwofei42 ...

  6. Python执行系统命令并获得输出的几种方法

    [root@a upfc]# ./ffmpeg-linux64-v3.3.1 -i a.mp3 ffmpeg version N-86111-ga441aa90e8-static http://joh ...

  7. 使用python执行系统命令——subprocess

     背景:subprocess是python官方推荐调用系统命令的模块 import subprocess subprocess最主要的两个方法/类: # 参数说明:stdin和stdout相当于一个管 ...

  8. Python执行系统命令:使用subprocess的Popen函数

    使用subprocess的Popen函数执行系统命令 参考: http://blog.sina.com.cn/s/blog_8f01450601017dlr.html http://blog.csdn ...

  9. Python执行系统命令的方法

    Python中执行系统命令常见方法有两种: 两者均需 import os (1) os.system # 仅仅在一个子终端运行系统命令,而不能获取命令执行后的返回信息 system(command) ...

  10. python执行系统命令后获取返回值

    import os, subprocess # os.system('dir') #执行系统命令,没有获取返回值,windows下中文乱码 # result = os.popen('dir') #执行 ...

随机推荐

  1. opencv基础

    Python 和 OpenCV 的结合是计算机视觉领域中应用最为广泛的一种方式,它们的结合使得开发者可以快速.高效地完成各种视觉任务.本文将介绍 Python 和 OpenCV 的基础使用,包括安装. ...

  2. 飞桨paddlespeech语音唤醒推理C实现

    上篇(飞桨paddlespeech 语音唤醒初探)初探了paddlespeech下的语音唤醒方案,通过调试也搞清楚了里面的细节.因为是python 下的,不能直接部署,要想在嵌入式上部署需要有C下的推 ...

  3. Node.js入门学习笔记

    NodeJs是js的运行时,意味着可以在浏览器外运行js.可以使用nodejs来构建服务器端应用.CLI应用.Web API,甚至用electron构建桌面端应用. 使用nvm来管理node版本. 在 ...

  4. 浅谈php GC(垃圾回收)机制及其与CTF的一点缘分

    0x00 侠客日常(一):CTF江湖试剑 众所周知,在php中,当对象被销毁时会自动调用__destruct()方法,同时也要知道,如果程序报错或者抛出异常,则就不会触发该魔术方法. 看题: < ...

  5. 2022-04-24:位集 Bitset 是一种能以紧凑形式存储位的数据结构。 请你实现 Bitset 类。 Bitset(int size) 用 size 个位初始化 Bitset ,所有位都是 0

    2022-04-24:位集 Bitset 是一种能以紧凑形式存储位的数据结构. 请你实现 Bitset 类. Bitset(int size) 用 size 个位初始化 Bitset ,所有位都是 0 ...

  6. 2022-02-04:组合总和 Ⅳ。 给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。 题目数据保证

    2022-02-04:组合总和 Ⅳ. 给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target .请你从 nums 中找出并返回总和为 target 的元素组合的个数. 题目数据保证 ...

  7. Bracket Sequence

    F. Bracket Sequence time limit per test 0.5 seconds memory limit per test 256 megabytes input standa ...

  8. sipp重放rtp数据测试FreeSWITCH

    环境:CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 sipp版本:3.6.1 一.背景描述 sipp是一款VoIP测试工具,日常开发过程中会使用到该软件,但其自身携带的pca ...

  9. 基于.NetCore开源的Windows的GIF录屏工具

    推荐一个Github上Start超过20K的超火.好用的屏幕截图转换为 GIF 动图开源项目. 项目简介 这是基于.Net Core + WPF 开发的.开源项目,可将屏幕截图转为 GIF 动画.它的 ...

  10. 更换Mysql数据库-----基于Abo.io 的书籍管理Web应用程序

    之前公司一直使用的是ASP.NET Boilerplate (ABP),但是当解决方案变得很大时,项目启动就变得非常慢,虽然也想了一些办法,将一些基础模块做成Nuget包的形式,让整个解决方案去引用. ...