首页 > 编程语言 >Python模块之subprocess

Python模块之subprocess

时间:2024-08-29 14:26:10浏览次数:11  
标签:ljk Python res None subprocess 模块 xr staff

转载:https://www.cnblogs.com/goldsunshine/p/17558075.html

 

subprocess 是 Python 中执行操作系统级别的命令的模块,所谓系级级别的命令就是如ls /etc/user ifconfig 等和操作系统有关的命令。
subprocess 创建子进程来执行相关命令,并连接它们的输入、输出和错误管道,获取它们的返回状态。

subprocess 来源


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

模块常用函数


函数清单总览


image

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文件,里面保存的是命令的输出结果
image

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模块可能更加适合。

 

标签:ljk,Python,res,None,subprocess,模块,xr,staff
From: https://www.cnblogs.com/xingxia/p/18386603/python_subprocess

相关文章

  • Python 中常用的人工智能库和工具
    在Python中,有许多强大的人工智能库和工具,它们为开发各种人工智能应用提供了有力的支持。以下是一些常用的人工智能库和工具介绍:一、机器学习库Scikit-learnScikit-learn是一个广泛应用于机器学习的Python库。它提供了各种经典的机器学习算法,包括分类、回归、聚类等......
  • 如何使用python实现批量ping,附代码
    以下为使用Python实现批量ping的多种方法及代码示例:方法一:importsubprocesswithopen('hosts.txt','r')asf:hosts=f.readlines()forhostinhosts:result=subprocess.check_output(('ping','-c','1',host.stri......
  • python+flask计算机毕业设计大学生闲置物品交易网站(程序+开题+论文)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展,电子商务已成为人们日常生活不可或缺的一部分,极大地促进了商品交易的便捷性与效率。然而,在大学校园内,一个普遍存......
  • python+flask计算机毕业设计二手车交易管理系统的设计与开发(程序+开题+论文)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着汽车消费市场的日益成熟与车辆更新换代速度的加快,二手车交易已成为汽车市场不可或缺的一部分。然而,传统二手车交易过程中存在信息不对......
  • python+flask计算机毕业设计基于的零食销售商城(程序+开题+论文)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着互联网的飞速发展,电子商务已成为现代商业不可或缺的一部分,深刻改变着人们的消费习惯。零食作为日常消费的重要组成部分,其市场潜力巨大......
  • LeetCode-Python-1539. 第 k 个缺失的正整数(二分)
    给你一个 严格升序排列 的正整数数组 arr 和一个整数 k 。请你找到这个数组里第 k 个缺失的正整数。示例1:输入:arr=[2,3,4,7,11],k=5输出:9解释:缺失的正整数包括[1,5,6,8,9,10,12,13,...]。第5个缺失的正整数为9。示例2:输入:arr=[1,2,3,4],k=2......
  • 探索 AI Agents:从理念到 Python 实际运用
    作者:老余捞鱼原创不易,转载请标明出处及原作者。写在前面的话:    本文主要介绍了如何利用人工智能代理(AIAgents)从概念到Python中的实际应用,以及如何构建一个内容创作工作流程,通过多个代理协作完成从视频分析到博客撰写的复杂任务,完成后也许这会改变你对人工智能......
  • 期权定价模型(如Black-Scholes模型)和利率模型中的单因子模型的Python实现案例
    一:期权定价模型(如Black-Scholes模型)的实现期权定价模型(如Black-Scholes模型)是用来确定期权合理价格的数学模型。这些模型基于一定的假设,考虑了多种因素,如标的资产价格、期权的行权价格、期权的到期时间、无风险利率以及标的资产的波动性等。接下来将使用Python来实现这个模......
  • Python 项目及依赖管理工具技术选型
    Python项目及依赖管理工具,类似于Java中的Maven与Node中的npm+webpack,在开发和维护项目时起着重要的作用。使用适当的依赖管理工具可以显著提高开发效率,减少依赖冲突,确保项目的稳定性、可靠性和安全性。一、常见项目及依赖管理工具需具备的功能1.依赖管理(1)自动化依赖......
  • python 包引入顺序
    isorthttps://pycqa.github.io/isort/isort·PyPIhttps://pypi.org/project/isort/Beforeisort:frommy_libimportObjectimportosfrommy_libimportObject3frommy_libimportObject2importsysfromthird_partyimportlib15,lib1,lib2,lib3,lib......