杰瑞科技汇

Python如何调用执行另一个Python脚本?

使用 os.system() (最简单,但功能有限)

这是最直接、最简单的方法,它就像在系统的命令行终端中直接输入命令一样。

Python如何调用执行另一个Python脚本?-图1
(图片来源网络,侵删)

工作原理os.system() 会启动一个子 shell(在 Windows 上是 cmd.exe,在 Linux/macOS 上是 /bin/sh)来执行你传入的字符串命令。

示例代码

script_to_call.py (被调用的脚本)

# 这是一个简单的被调用脚本
import sys
import time
print("被调用脚本开始执行...")
print(f"接收到的参数数量: {len(sys.argv)}")
for i, arg in enumerate(sys.argv):
    print(f"参数 {i}: {arg}")
time.sleep(2) # 模拟耗时操作
print("被调用脚本执行完毕!")

main_script.py (主调用脚本)

import os
print("主脚本开始...")
# 构建要执行的命令字符串
# 注意:在 Windows 上使用 python,在 Linux/macOS 上使用 python3
command = 'python script_to_call.py arg1 "arg with space"'
print(f"准备执行命令: {command}")
# 执行命令
return_code = os.system(command)
print(f"\n命令执行完毕,返回码: {return_code}")
print("主脚本结束。")

如何运行

# 在终端中运行主脚本
python main_script.py

输出

主脚本开始...
准备执行命令: python script_to_call.py arg1 "arg with space"
被调用脚本开始执行...
接收到的参数数量: 3
参数 0: script_to_call.py
参数 1: arg1
参数 2: arg with space
被调用脚本执行完毕!
命令执行完毕,返回码: 0
主脚本结束。

优点

  • 非常简单,一行代码就能搞定。
  • 适用于简单的、独立的脚本调用。

缺点

  • 无法直接获取子脚本的输出os.system() 只能返回命令的退出码(return_code),无法捕获 printsys.stdout 的输出。
  • 性能较差:需要创建一个完整的子 shell 进程,开销较大。
  • 跨平台兼容性:路径分隔符(\ vs )和命令格式在不同操作系统上可能需要调整。
  • 安全性:如果命令字符串来自用户输入,容易导致命令注入漏洞。

使用 subprocess 模块 (推荐,功能最强大)

subprocess 是 Python 官方推荐的用于创建和管理子进程的模块,它旨在替代 os.system()os.spawn* 等旧方法,提供了更强大、更灵活、更安全的接口。

subprocess 有多种函数,最常用的是 run()

1 subprocess.run() (推荐用法)

这是 Python 3.5+ 引入的现代、推荐的接口。

示例代码

main_script.py (使用 subprocess.run)

import subprocess
print("主脚本开始...")
# 要执行的命令,推荐使用列表形式,更安全
command = ['python', 'script_to_call.py', 'arg1', 'arg with space']
# 使用 run() 函数执行
# capture_output=True: 捕获标准输出和标准错误
# text=True: 将输出解码为文本(字符串),否则是字节
# check=True: 如果子进程返回非零退出码(表示出错),则抛出 CalledProcessError 异常
try:
    result = subprocess.run(command, capture_output=True, text=True, check=True, timeout=5)
    print("\n--- 调用成功 ---")
    print(f"返回码: {result.returncode}")
    print("标准输出:")
    print(result.stdout) # result.stdout 是一个字符串
except subprocess.CalledProcessError as e:
    print(f"\n--- 调用失败,返回码 {e.returncode} ---")
    print("标准错误:")
    print(e.stderr)
except subprocess.TimeoutExpired:
    print("\n--- 调用超时 ---")
print("主脚本结束。")

优点

  • 功能强大:可以轻松捕获标准输出、标准错误、返回码。
  • 安全性高:推荐使用列表形式传递命令和参数,可以避免 shell 注入问题。
  • 灵活性高:可以设置超时、工作目录、环境变量等。
  • 跨平台性好

缺点

  • 相比 os.system() 代码稍多,但这是为了换取强大功能。

2 subprocess.Popen() (最底层,最灵活)

Popensubprocess 的底层构造函数,run() 也是基于它封装的,当你需要更精细地控制子进程的生命周期时(与子进程进行实时交互),就需要使用 Popen

示例代码

main_script.py (使用 subprocess.Popen)

import subprocess
import sys
print("主脚本开始...")
command = ['python', 'script_to_call.py', 'arg1', 'arg with space']
# 使用 Popen 启动进程
# stdout=subprocess.PIPE: 捕获标准输出
# stderr=subprocess.PIPE: 捕获标准错误
# text=True: 以文本模式处理流
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# 可以在这里做其他事情,等待子进程完成
print("子进程已启动,主脚本正在等待其完成...")
# 等待进程结束,并获取输出和返回码
# communicate() 会读取所有输出并等待进程结束
stdout, stderr = process.communicate()
print("\n--- 调用结束 ---")
print(f"返回码: {process.returncode}")
print("标准输出:")
print(stdout)
if stderr:
    print("标准错误:")
    print(stderr)
print("主脚本结束。")

优点

  • 极致的灵活性:可以启动进程后立即返回,然后通过 poll() 检查状态,或者通过 communicate()stdin.write() 等方法与子进程进行双向交互。
  • 适合需要复杂交互或并发控制的场景。

缺点

  • 使用比 run() 更复杂,容易出错。

将脚本作为模块导入

如果你的两个脚本紧密相关,并且你希望共享变量和函数,而不是像两个独立程序那样运行,那么导入模块是最佳选择。

工作原理:直接将另一个脚本作为 Python 模块导入,然后调用其中的函数或直接执行其顶层代码。

示例代码

script_to_call.py (被调用的模块)

# 这是一个可以被导入的模块
print("模块 script_to_call.py 被导入。")
# 定义一些可以被调用的函数
def greet(name):
    """一个简单的问候函数"""
    return f"你好, {name}! 欢迎使用模块功能。"
# 脚本顶层代码,在被导入时也会执行
if __name__ == "__main__":
    # 这部分代码只有在直接运行此脚本时才会执行
    # 如果是被导入,则不会执行
    print("script_to_call.py 作为主程序直接运行。")
    print("接收到的参数:", sys.argv[1:])

main_script.py (主调用脚本)

import sys
import script_to_call # 直接导入模块
print("主脚本开始...")
# 调用被导入模块的函数
message = script_to_call.greet("Alice")
print(message)
# 被导入模块的顶层代码(if __name__ == "__main__" 之外的部分)在导入时就已经执行了
# 所以你会在主脚本的开头看到 "模块 script_to_call.py 被导入。"
print("主脚本结束。")

如何运行

python main_script.py

输出

模块 script_to_call.py 被导入。
主脚本开始...
你好, Alice! 欢迎使用模块功能。
主脚本结束。

优点

  • 代码复用性高:可以共享函数、类和变量。
  • 性能好:没有创建新进程的开销。
  • 调试方便:可以在同一个 IDE 中调试所有代码。

缺点

  • 耦合度高:两个脚本不再是独立的,修改 script_to_call.py 的顶层逻辑可能会影响 main_script.py
  • 不适合独立任务script_to_call.py 是一个完整的、应该独立运行的任务(例如数据处理、训练模型),将其导入会破坏其独立性。

总结与如何选择

方法 优点 缺点 适用场景
os.system() 极其简单 功能弱,无法获取输出,不安全,性能差 快速测试,调用非常简单的、无参数的独立脚本。不推荐用于生产环境
subprocess.run() 功能强大安全,灵活,可获取输出/错误/返回码 代码稍多 强烈推荐,绝大多数需要调用外部脚本或命令的场景,尤其是需要传递参数和获取结果时。
subprocess.Popen() 最灵活,可交互,并发控制 复杂,易用性低 需要与子进程进行实时I/O交互(如SSH连接、游戏引擎),或需要精细管理多个子进程时。
模块导入 代码复用,高性能,耦合 耦合度高,破坏脚本独立性 两个脚本属于同一个项目,功能紧密相关,需要共享函数和变量的情况。

决策建议

  1. 如果你只是想快速运行一个独立的脚本,不关心结果:可以用 os.system(),但请优先考虑更现代的方式。
  2. 如果你需要传递参数,并且需要知道脚本是否成功执行,或者需要获取它的输出结果请使用 subprocess.run(),这是最通用、最推荐的方案。
  3. 如果你需要一边运行子脚本,一边实时处理它的输出:使用 subprocess.Popen()
  4. 如果你的两个脚本本质上就是同一个程序的两个部分,它们需要共享代码和数据:使用模块导入的方式。
分享:
扫描分享到社交APP
上一篇
下一篇