浅谈Python调用Shell脚本的三种常用方式
作者:BSPMaestroX
一、通过 Python 来调用 Shell 脚本的三种常用方式
三种方式的优缺点
| os.system | subprocess.run | subprocess.Popen | |
|---|---|---|---|
| 是否需要解析参数 | no | yes | yes |
| 同步执行(等待Shell执行结果) | yes | yes | no |
| 能够获得 shell 的输入和输出 | no | yes | yes |
| Shell 执行结果返回值 | return value | object | object |
os.system
import os
return_code=os.system('ls -al .')
print(return_code)
/home/topeet/miniconda3/envs/rknn/bin/python3 /home/topeet/test/python/test.py
总用量 20
drwxrwxr-x 3 topeet topeet 4096 8月 11 00:56 .
drwxrwxr-x 6 topeet topeet 4096 7月 27 06:27 …
drwxrwxr-x 3 topeet topeet 4096 8月 11 00:36 .idea
-rw-rw-r-- 1 topeet topeet 504 7月 27 07:35 main.py
-rwxrwxr-x 1 topeet topeet 2442 8月 11 00:56 test.py
0
也会将Shell语句的输出输出到的 Python的命令控制台中。
但是 Python 的能够获取的返回值,是数字,0 代表 Shell 语句/脚本的执行成功,否则表示Shell执行的状态值。
适用于不需要详细的返回信息的 shell 脚本
传入 Shell 命令的参数,都是完整的 String。
subprocess.run(推荐)
1 、 能够相当方便的控制 shell 命令的输入和输出
- 1 传入的参数是一个 数组 而不是字符串。
import subprocess return_code=subprocess.run(['ls','-al','.']) print(return_code)
/home/topeet/miniconda3/envs/rknn/bin/python3 /home/topeet/test/python/test.py
总用量 20
drwxrwxr-x 3 topeet topeet 4096 8月 11 01:00 .
drwxrwxr-x 6 topeet topeet 4096 7月 27 06:27 …
drwxrwxr-x 3 topeet topeet 4096 8月 11 00:36 .idea
-rw-rw-r-- 1 topeet topeet 504 7月 27 07:35 main.py
-rwxrwxr-x 1 topeet topeet 2533 8月 11 01:00 test.py
CompletedProcess(args=['ls', '-al', '.'], returncode=0)
- 2 执行返回的结果是一个CompletedProcess对象
2 、忽略shell 脚本执行的结果
import subprocess return_code=subprocess.run(['ls','-al','.'],stdout=subprocess.DEVNULL) print(return_code)
(base) topeet@ubuntu:~/test/python$ python test.py
CompletedProcess(args=['ls', '-al', '.'], returncode=0)
- 2 输出结果就仅有 Python 程序运行出的结果。
3 、指定 shell 命令的输入参数(通过 input 参数来传入)
import subprocess useless_cat_call=subprocess.run(['cat'],stdout=subprocess.PIPE,text=True,input="Hello") print(useless_cat_call.stdout)
/home/topeet/miniconda3/envs/rknn/bin/python3 /home/topeet/test/python/test.py
Hello
- stdout=subprocess.PIPE 告诉 Python,重定向 Shell 命令的输出,并且这部分输出可以被后面的 Python 程序所调用。
- text=True 告诉 Python,将 shell 命令的执行的 stdout 和 stderr 当成字符串. 默认是当成 bytes.
- input="Hello from the other side" 告诉 Python,shell 命令的输入参数.
4 、开启 shell 命令的执行校验
- 1 当 shell 的执行出现任何问题,都会抛出一个异常。
import subprocess
failed_command=subprocess.run(['false'],text=True)
print("The exit code was: %d" % failed_command.returncode)
/home/topeet/miniconda3/envs/rknn/bin/python3 /home/topeet/test/python/test.py
The exit code was:1
5、subprocess.run方法参数的介绍
核心控制参数
- args
- 作用:指定要执行的命令,支持列表(推荐)或字符串形式。
- 注意:列表形式可避免命令注入风险;字符串形式必须设置 shell=True。
subprocess.run(["ls", "-l"]) # 安全方式(列表)[1,3]
subprocess.run("ls -l", shell=True) # 字符串需配合 shell=True[5]
- shell
- 作用:是否通过系统 Shell(如 /bin/sh或 cmd.exe)执行命令。
- 风险:shell=True可能引发安全漏洞(如执行恶意输入),非必要不启用
- 适用场景:需 Shell 特性(如通配符 *、管道 |)时使用。
- timeout
- 作用:设置命令超时时间(秒),超时抛出 TimeoutExpired异常。
- 示例:
try:
subprocess.run(["sleep", "10"], timeout=5)
except TimeoutExpired:
print("命令超时!") # 5秒后中断[3,6]
输入/输出处理参数
- stdin、stdout、stderr
- 作用:控制子进程的标准流,可选值:
- subprocess.PIPE:捕获数据流(通过 result.stdout访问)
- subprocess.DEVNULL:丢弃输出(类似 /dev/null)。
- 文件对象:重定向到文件。
- 示例:
result = subprocess.run(["ls"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- capture_output
- 作用:简化输出捕获(等价于设置 stdout=PIPE, stderr=PIPE)。
- 冲突:不可与 stdout/stderr同时使用
- input
- 作用:向子进程的 stdin 传递数据,需配合 stdin=PIPE(自动启用)。
- 数据类型:
- text=True时:字符串(如 input="Hello")
- 默认:字节流(如 input=b"Hello")。
- text/ encoding
- 作用:控制输入/输出的文本模式:
- text=True:输入/输出自动转为字符串(等价于 universal_newlines=True)
- encoding="utf-8":指定编解码方式(如处理中文)。
执行环境参数
- cwd
- 作用:设置子进程的工作目录。
- 示例:
subprocess.run(["pwd"], cwd="/tmp") # 输出 "/tmp
- env
- 作用:自定义环境变量(字典形式),默认继承父进程环境。
- 示例:
env = {"PATH": "/usr/bin", "MY_VAR": "test"}
subprocess.run(["echo", "$MY_VAR"], env=env, shell=True)[4](@ref)
异常与状态处理
- check
- 作用:若子进程返回非零退出码,抛出 CalledProcessError异常。
- 示例:
try:
subprocess.run(["false"], check=True) # 总是失败的命令
except CalledProcessError as e:
print(f"错误码: {e.returncode}, 错误: {e.stderr}")
- 返回值 CompletedProcess
- 属性:
- returncode:退出状态码(0 表示成功)。
- stdout/stderr:捕获的输出(文本或字节流)。
- args:执行的命令
- 方法:
- check_returncode():非零退出码时抛出异常。
最佳实践总结
- 安全优先:
- 使用列表传参(如 ["ls", "-l"]),避免 shell=True除非必要
- 输出处理:
- 需捕获输出时,用 capture_output=True+ text=True简化代码。
- 异常管理:
- 关键命令添加 check=True+ try/except确保失败可追溯
- 跨平台注意:
- Windows 部分命令(如 dir)需 shell=True
- 性能优化:
- 避免频繁调用高开销命令(如反复启动 Shell)
subprocess.Popen
1 、subprocess.Popen能够提供更大的灵活性。
subprocess.run 可以看成是 subprocess.Popen 一个简化抽象。
2 、subprocess.Popen的使用与注意
1.默认情况下, subprocess.Popen 不会中暂停 Python 程序本身的运行(异步)
2.但是如果你非得同前面两种方式一样,同步运行,你可以加上 .wait() 方法。
3.当我们仍然处在异步的状况,通过 poll() 来轮询,知道shell 命令是否运行结束与否?当放回结果为 None,则表示程序还在运行,否者会返回一个状态码。
如果想, 指定输入参数 ,则需要通过 communicate() 方法。
import subprocess useless_cat_call=subprocess.Popen(['cat'],stdout=subprocess.PIPE,stdin=subprocess.PIPE,stderr=subprocess.PIPE,text=True) output,errors=useless_cat_call.communicate(input="hello") useless_cat_call.wait() print(output) print(errors)
/home/topeet/miniconda3/envs/rknn/bin/python3 /home/topeet/test/python/test.py
hello
到此这篇关于浅谈Python调用Shell脚本的三种常用方式的文章就介绍到这了,更多相关Python调用Shell脚本内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
