PowerShell 5 下 Python 脚本 JSON 传参的终极方案:管道 stdin

PowerShell 5 下 Python 脚本 JSON 传参的终极方案:管道 stdin

问题背景

在 Windows PowerShell 5 环境下,AI 工具调用 Python 脚本传递 JSON 参数时,存在三大痛点:

  1. 引号被吞噬:PowerShell 5 解析命令行参数时会自作主张地剥离双引号
  2. 文件传参混乱:被迫使用配置文件传参,多进程并发时互相覆盖
  3. 中文编码乱码:代码页 936(GBK)与 Python UTF-8 不兼容

引号吞噬实测

# 你在终端里敲的:
python script.py '{"test": 1}'

# PowerShell 5 实际传给 Python 的:
# sys.argv = ['script.py', '{test: 1}']  ← 双引号没了!

管道 stdin 方案

核心思路:用 echo + 管道 | 把 JSON 传给 Python 的 sys.stdin,完全绕过 PowerShell 的命令行参数解析。

echo '{"name": "桂良涛", "age": 30}' | python script.py

为什么管道能绕过?

PowerShell 的单引号是字面量字符串,内部不做任何转义、变量替换。所以 echo '{"key": "value"}' 中间的双引号原封不动地传到 Python 的 stdin。

踩坑:两个隐藏的编码陷阱

陷阱 1:不要用 sys.stdin.read()

# ❌ 错误写法
raw = sys.stdin.read()  # 依赖 sys.stdin.encoding,中文变 ???

PowerShell 5 管道实际发的是 GBK 编码字节,但 Python 声称 stdin 是 UTF-8,导致中文全部变成 ???

陷阱 2:UTF-8 BOM 头

PowerShell 5 管道传输 UTF-8 内容时会加 BOM 头(EF BB BF),json.loads() 直接报 Unexpected UTF-8 BOM

正确写法

import sys
import json

def read_stdin() -> str:
    raw_bytes = sys.stdin.buffer.read()  # 读原始字节,不经过编码层
    for enc in ("utf-8-sig", "utf-8", "gbk"):  # utf-8-sig 自动去掉 BOM
        try:
            return raw_bytes.decode(enc).strip()
        except (UnicodeDecodeError, ValueError):
            continue
    return raw_bytes.decode("utf-8", errors="replace").strip()

if not sys.stdin.isatty():
    params = json.loads(read_stdin())

关键点:

  • sys.stdin.buffer.read() 读原始字节,绕过 Python 的编码推断
  • utf-8-sig 解码器自动处理 BOM 头
  • utf-8-sig → utf-8 → gbk 顺序尝试解码,兼容所有环境

实测验证

设计 10 个测试用例覆盖所有刁钻场景:

# 测试场景 结果
1 中文键值 PASS
2 嵌套对象 + 中文 PASS
3 数组参数 PASS
4 值内含双引号 \" PASS
5 反斜杠路径 + 正则 PASS
6 中文 + 换行符 \n PASS
7 URL 参数 ?a=1&b=2 PASS
8 布尔 / null PASS
9 Emoji 表情 PASS
10 复杂嵌套(数组+对象+中文+特殊字符+浮点) PASS

10/10 全部通过,覆盖了 API 调试、数据库查询等所有实际使用场景。

标准脚本模板

所有 Python 工具脚本统一使用以下模式:

import sys
import json

def read_stdin() -> str:
    raw_bytes = sys.stdin.buffer.read()
    for enc in ("utf-8-sig", "utf-8", "gbk"):
        try:
            return raw_bytes.decode(enc).strip()
        except (UnicodeDecodeError, ValueError):
            continue
    return raw_bytes.decode("utf-8", errors="replace").strip()

def main():
    action = sys.argv[1] if len(sys.argv) > 1 else None
    params = {}
    if not sys.stdin.isatty():
        params = json.loads(read_stdin())
    
    # ... 业务逻辑

if __name__ == "__main__":
    main()

调用方式:

# 标准调用
echo '{"url": "/api/test", "method": "POST", "params": {"id": 1}}' | python script.py action_name

# 无参数调用
echo '{}' | python script.py action_name

对比文件传参

维度 文件传参 管道 stdin
并发安全 ❌ 多进程写同一文件互相覆盖 ✅ 每个进程独立 stdin
清理负担 ❌ 要手动删临时文件 ✅ 无残留
可读性 ❌ 要先看文件内容才知道参数 ✅ 命令本身就能看到
AI 调用友好度 ❌ 要写文件再执行两步 ✅ 一行命令搞定
引号转义 ✅ 绕过了(文件不经 PS 解析) ✅ 绕过了(stdin 不经 PS argv 解析)
中文支持 ✅ 文件编码可控 ✅ utf-8-sig 解码

结论

管道传参 + sys.stdin.buffer.read() + utf-8-sig 解码 = 彻底解决 PowerShell 5 下 Python 脚本 JSON 传参的一切痛点。文件传参可以完全废弃。

作者:桂良涛
联系方式:桂良涛@nndrobot.com
日期:2026-05-18

posted @ 2026-05-18 10:20  gltttt  阅读(43)  评论(0)    收藏  举报