杰瑞科技汇

Python执行调用Python如何传参?

方法 命令/函数 优点 缺点 适用场景
subprocess 模块 subprocess.run() 现代、灵活、安全、功能强大,是官方推荐的方式 语法相对复杂一点 绝大多数情况下的首选,可以获取子进程的输出、状态码,控制输入输出等。
os 模块 os.system() 非常简单,一行代码就能执行 功能有限,不安全(有命令注入风险),难以获取子进程的输出 快速执行简单命令,不关心输出和返回值,且参数完全可控的场景。
exec() 函数 exec(open('file').read()) 在同一进程中执行,性能高,内存占用低 会覆盖当前进程的命名空间,原脚本执行完后,新脚本的内容会完全替换当前脚本 动态加载和执行代码块,或者你想在当前脚本中“嵌入”并运行另一个脚本的逻辑。

使用 subprocess 模块 (最推荐)

subprocess 模块是Python用来创建和管理子进程的强大工具。subprocess.run() 是Python 3.5+引入的通用函数,是执行命令的推荐方式。

Python执行调用Python如何传参?-图1
(图片来源网络,侵删)

基本用法:执行脚本并传递参数

假设我们有两个文件:

script_to_call.py (被调用的脚本) 这个脚本会接收命令行参数并打印出来。

# script_to_call.py
import sys
import argparse
# 方法一:直接使用 sys.argv
print("--- 通过 sys.argv 接收参数 ---")
print(f"脚本名称: {sys.argv[0]}")
print(f"所有参数列表: {sys.argv[1:]}")
print(f"第一个参数: {sys.argv[1]}")
print(f"第二个参数: {sys.argv[2]}")
# 方法二:使用 argparse 模块(更健壮,推荐)
print("\n--- 通过 argparse 接收参数 ---")
parser = argparse.ArgumentParser(description="这是一个接收参数的示例脚本。")
parser.add_argument('--name', type=str, required=True, help="你的名字")
parser.add_argument('--age', type=int, help="你的年龄")
parser.add_argument('--verbose', action='store_true', help="是否打印详细信息")
args = parser.parse_args()
print(f"你好, {args.name}!")
if args.age:
    print(f"你今年 {args.age} 岁了。")
if args.verbose:
    print("详细模式已开启。")

main_script.py (主调用脚本) 这个脚本会调用上面的脚本并传递参数。

# main_script.py
import subprocess
import sys
# 定义要执行的命令和参数
# 命令是一个列表,每个元素都是一个独立的参数,这样可以避免shell注入的风险
command = [
    'python', 
    'script_to_call.py',
    '--name', 'Alice',
    '--age', '30',
    '--verbose'
]
# 使用 subprocess.run() 执行命令
# capture_output=True 会捕获标准输出和标准错误
# text=True 会将输出解码为文本
# check=True 如果子进程返回非零状态码(即出错),则会抛出 CalledProcessError 异常
try:
    print(f"正在执行命令: {' '.join(command)}")
    result = subprocess.run(command, capture_output=True, text=True, check=True)
    # 打印子进程的标准输出
    print("\n--- 子进程的标准输出 ---")
    print(result.stdout)
    # 打印子进程的返回码 (正常情况下为0)
    print(f"\n子进程返回码: {result.returncode}")
except subprocess.CalledProcessError as e:
    print(f"执行命令时出错!返回码: {e.returncode}")
    print(f"错误输出:\n{e.stderr}")
except FileNotFoundError:
    print("错误: 未找到 'python' 命令或 'script_to_call.py' 文件,请确保它们在当前目录下。")

如何运行: 在终端中,确保两个文件在同一个目录下,然后运行主脚本:

Python执行调用Python如何传参?-图2
(图片来源网络,侵删)
python main_script.py

预期输出:

正在执行命令: python script_to_call.py --name Alice --age 30 --verbose
--- 子进程的标准输出 ---
--- 通过 sys.argv 接收参数 ---
脚本名称: script_to_call.py
所有参数列表: ['--name', 'Alice', '--age', '30', '--verbose']
第一个参数: --name
第二个参数: Alice
--- 通过 argparse 接收参数 ---
你好, Alice!
你今年 30 岁了。
详细模式已开启。
子进程返回码: 0

subprocess.run() 的关键参数

  • args: 要执行的命令,推荐使用列表形式,因为它更安全(可以防止shell注入),也更清晰。
  • capture_output=True: 捕获标准输出和标准错误,如果设为 False(默认),输出会直接显示在终端。
  • text=True: 将捕获到的输出作为字符串返回,如果为 False(默认),输出会是字节流。
  • check=True: 如果子进程返回非零退出码(表示出错),则抛出 CalledProcessError 异常,如果设为 False(默认),即使出错也会正常返回。
  • shell=False: 是否通过shell执行,默认为 False,设为 True 可以使用shell特性(如管道 ),但有安全风险。
  • input: 向子进程的标准输入传递字符串或字节。

使用 os 模块

os.system() 是一个非常老派的函数,简单直接,但功能和安全方面都不如 subprocess

main_script_os.py

# main_script_os.py
import os
# 命令以字符串形式给出
# 注意:这种方式有命令注入风险,如果参数来自用户输入,非常危险!
command_str = "python script_to_call.py --name Bob --age 25"
print(f"正在执行命令: {command_str}")
return_code = os.system(command_str)
print(f"\n命令执行完毕,返回码: {return_code}")

如何运行:

Python执行调用Python如何传参?-图3
(图片来源网络,侵删)
python main_script_os.py

预期输出:

正在执行命令: python script_to_call.py --name Bob --age 25
--- 通过 sys.argv 接收参数 ---
脚本名称: script_to_call.py
所有参数列表: ['--name', 'Bob', '--age', '25']
第一个参数: --name
第二个参数: Bob
--- 通过 argparse 接收参数 ---
你好, Bob!
你今年 25 岁了。
命令执行完毕,返回码: 0

缺点:

  1. 不安全:如果参数来自用户输入,name = input("请输入名字: "),恶意用户可以输入 rm -rf /,导致 command_str 变成 python script_to_call.py --name rm -rf /,造成严重破坏。
  2. 难以获取输出os.system() 只返回命令的退出码,无法获取标准输出或标准错误的内容。

使用 exec() 函数

exec() 会在当前进程中执行代码,而不是创建一个新的子进程,这会带来巨大的副作用。

main_script_exec.py

# main_script_exec.py
print("这是主脚本开始执行的地方。")
# 准备要执行的代码
# 注意:这里我们直接读取并执行文件内容,而不是传参
# 传参需要更复杂的动态构造代码字符串的方式
code_to_run = """
# 这是一个动态执行的代码块
print("这是通过 exec() 执行的代码块。")
import sys
print(f"在 exec 中的 sys.argv: {sys.argv}") # sys.argv 会是调用 exec 的脚本的参数
"""
print("\n--- 即将通过 exec() 执行代码 ---")
exec(code_to_run)
print("\n这是主脚本执行结束的地方。")
# 注意:在执行完 exec 后,如果被 exec 的代码定义了变量,这些变量会存在于当前命名空间中

如何运行:

python main_script_exec.py

预期输出:

这是主脚本开始执行的地方。
--- 即将通过 exec() 执行代码 ---
这是通过 exec() 执行的代码块。
在 exec 中的 sys.argv: ['main_script_exec.py']
这是主脚本执行结束的地方。

重要警告: exec() 会完全污染当前脚本的命名空间,如果你执行的外部文件定义了函数或变量,它们会覆盖当前脚本的同名函数或变量,且在 exec 执行后依然存在。

script_with_definitions.py

def greet(name):
    print(f"Hello from the external script, {name}!")
VERSION = "1.0"

main_script_exec_dangerous.py

import sys
# 被调用的脚本
script_path = 'script_with_definitions.py'
# 动态读取并执行
with open(script_path, 'r') as f:
    code = f.read()
print("主脚本中定义的函数:")
def greet(name):
    print(f"Hello from the main script, {name}!")
VERSION = "original"
print(f"主脚本中的 VERSION: {VERSION}")
greet("Main User")
print("\n--- 执行外部脚本 ---")
exec(code)
print("\n执行外部脚本后:")
print(f"当前的 VERSION: {VERSION}") # 注意,VERSION 被覆盖了!
greet("Main User Again") # 注意,greet 函数也被覆盖了!

运行 main_script_exec_dangerous.py 会看到 VERSIONgreet 函数都被外部脚本覆盖了,这就是为什么 exec 通常用于执行你完全信任的、动态生成的代码片段,而不是执行一个独立的、可能存在冲突的脚本。


总结与最佳实践

场景 推荐方法 理由
绝大多数情况 subprocess.run() 功能最全、最安全、最灵活,可以获取输出、处理错误、控制输入输出,是现代Python的标准做法。
快速执行、简单命令 os.system() 代码最简单,但只适用于参数完全由你控制、不关心输出的情况。不推荐用于生产环境
在同一进程中运行逻辑 exec() 性能高,但会污染和覆盖当前命名空间,风险极高,除非有特殊需求(如动态加载插件),否则应避免使用。

最终建议: 除非有非常特殊的需求,否则请始终使用 subprocess.run() 来调用其他Python脚本或外部命令,它为你提供了对子进程的完全、安全且强大的控制。

分享:
扫描分享到社交APP
上一篇
下一篇