Python调用外部脚本时怎么传递参数和获取返回值

江岸
江岸
发布时间:2026-10-23| 4人浏览过
原创

在 Python 开发中,调用外部脚本(无论是另一个 Python 脚本、Shell 脚本,还是已编译的二进制可执行文件)的标准现代化解决方案是标准库中的 subprocess.run()。传参的关键在于将命令与参数组织为纯字符串列表直接传入,以避免 Shell 注入和解析歧义;而获取返回值则区分为两层:一层是操作系统层面的执行状态码(returncode),另一层是子进程输出到标准输出(stdout)或标准错误(stderr)的实际业务数据流。

Python调用外部脚本时怎么传递参数和获取返回值
图 1:主进程与被调用外部脚本之间的数据交互架构

一、核心机制:进程如何传递参数与产生返回

在操作系统的进程树模型中,父进程启动子进程时,参数通过进程创建 API(在类 Unix 系统中为 execve,在 Windows 中为 CreateProcess)传递。子进程被注入一个指针数组,对应 Python 语言中的 sys.argv 列表。

当外部脚本完成工作后,它不能直接向父进程返回 Python 内存对象(例如字典或列表)。外部脚本能够留给父进程的信息仅有两类:

  1. 进程退出状态码(Exit Code):一个 0 到 255 范围的整数。按照工业标准,0 表示进程正常退出,非 0 值(如 1、2、127 等)表示遇到异常或特定错误分支。
  2. 标准输出与标准错误流(stdout / stderr):子进程在终端打印的所有文本数据。父进程可以通过管道捕获并解析为字符串或反序列化为结构化对象。

二、标准调用四步实现法

为了让交互流程清晰可测,我们分别编写被调用的目标脚本 worker.py 和负责调度的主控脚本 caller.py。

Python调用外部脚本时怎么传递参数和获取返回值
图 2:调用外部脚本的四个关键步骤

步骤一:编写外部脚本 worker.py

外部脚本通过 sys.argv 接收参数。由于参数在操作系统层面都是纯字符串,如果需要数值必须手动转换类型;脚本使用标准输出(print)输出业务处理结果,通过 sys.exit(code) 返回状态码。

# 文件名:worker.py
import sys
import json

def calculate():
    # sys.argv[0] 是脚本名称自身,后续元素才是真正传入的参数
    if len(sys.argv) < 3:
        sys.stderr.write("错误:需要提供至少两个数值参数。\n")
        sys.exit(1)  # 状态码 1 表示参数缺失异常

    try:
        num1 = float(sys.argv[1])
        num2 = float(sys.argv[2])
    except ValueError:
        sys.stderr.write("错误:参数必须能转换为数字。\n")
        sys.exit(2)  # 状态码 2 表示参数格式非法

    # 业务处理
    result = {
        "status": "success",
        "inputs": [num1, num2],
        "sum": num1 + num2,
        "product": num1 * num2
    }

    # 将结果转换为 JSON 格式打印到标准输出
    print(json.dumps(result))
    sys.exit(0)  # 状态码 0 表示成功

if __name__ == "__main__":
    calculate()

步骤二:编写主控脚本 caller.py 发起调用

在主进程中使用 subprocess.run() 来执行 worker.py,通过指定 capture_output=True 和 text=True 将输出自动解码为 Python 字符串。

# 文件名:caller.py
import sys
import json
import subprocess

def run_external_worker():
    # 推荐使用 sys.executable 获取当前相同的 Python 解释器路径
    cmd = [sys.executable, "worker.py", "15.5", "4"]

    print(f"正在执行命令: {' '.join(cmd)}")
    
    # 调用外部脚本并捕获输出
    result = subprocess.run(
        cmd,
        capture_output=True,  # 捕获 stdout 和 stderr
        text=True,            # 以文本字符串形式返回(而非 bytes)
        check=False           # 先不抛出 CalledProcessError,手动判定状态
    )

    # 1. 检查退出状态码
    if result.returncode != 0:
        print(f"执行失败,退出码: {result.returncode}")
        print(f"错误输出 (stderr):\n{result.stderr.strip()}")
        return

    # 2. 获取并解析标准输出内容
    raw_output = result.stdout.strip()
    try:
        data = json.loads(raw_output)
        print("调用成功,收到解析后的返回值:")
        print(f"两数之和: {data['sum']}")
        print(f"两数之积: {data['product']}")
    except json.JSONDecodeError:
        print(f"输出非合法 JSON: {raw_output}")

if __name__ == "__main__":
    run_external_worker()

在终端运行 python caller.py 后的控制台示例输出如下:

正在执行命令: /usr/bin/python3 worker.py 15.5 4
调用成功,收到解析后的返回值:
两数之和: 19.5
两数之积: 62.0

三、两种返回值的本质区别与选择依据

许多初学者容易混淆 returncode 和 stdout。下表清晰列举了两者的定义、承载能力及适用场景:

Python调用外部脚本时怎么传递参数和获取返回值
图 3:returncode 与 stdout 的特性与定位对比

四、关键细节与避坑指南

安全第一:为什么强烈禁止 shell=True?

当设置 shell=True 时,Python 会启动系统的 Shell(如 /bin/sh 或 cmd.exe)来执行命令字符串。如果传递的参数来自用户输入,且包含 ;、&& 或 |,攻击者就可以随意拼接执行任意恶意指令(Shell 注入漏洞)。

通过使用列表参数传递(shell=False 为默认值),操作系统会把每个列表元素直接作为独立参数送给子进程,无需经过 Shell 解析拆解,从底层彻底根除了参数注入风险。

1. 命名参数与标志位的传递

如果被调用脚本采用类似 argparse 的规范解析参数,传参列表需按顺序将参数名与值拆分成两个元素:

# 错误示例:不要把标志与值拼在一个字符串中
cmd = [sys.executable, "task.py", "--batch-size 64"]

# 正确示例:每个标志与值作为独立的列表项
cmd = [
    sys.executable, 
    "task.py", 
    "--mode", "production",
    "--batch-size", "64",
    "--verbose"
]

2. 编码与换行符处理

在 Windows 环境下,外部脚本如果打印中文字符,子进程默认可能使用 GBK 或 CP936 编码,而主进程若假定 UTF-8 可能导致 UnicodeDecodeError。此时应显式声明编码:

result = subprocess.run(
    cmd,
    capture_output=True,
    text=True,
    encoding="utf-8",       # 明确指定编码,避免跨平台解析错乱
    errors="replace"        # 遇到无法解码的字符时替换,不中断主流程
)

3. 自动化异常抛出与超时控制

如果外部脚本可能出现死循环,调用时应设置 timeout。若希望在外部脚本非 0 退出时直接中断报错,可开启 check=True:

try:
    # 限制外部脚本必须在 10 秒内执行完毕
    res = subprocess.run(cmd, capture_output=True, text=True, check=True, timeout=10)
    print(res.stdout)
except subprocess.TimeoutExpired:
    print("错误:外部脚本执行超时!")
except subprocess.CalledProcessError as e:
    print(f"脚本执行失败,退出码: {e.returncode}")
    print(f"错误内容: {e.stderr}")

五、场景选型总结

根据具体工程需求的不同,选择最合适的数据通信方式:

Python调用外部脚本时怎么传递参数和获取返回值
图 4:技术选型与通信架构决策路径

综上所述,Python 调用外部脚本的最佳实践可概括为三点:列表形式传参确保安全、capture_output 搭配 text=True 捕获文本输出、通过 returncode 和 JSON stdout 组合实现可靠的异常与数据流处理。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。