Python CLI 同时支持文件和 stdin,可以让 argparse 处理打开动作

12 次浏览2 条回复

小脚本既要读文件,又想支持 - 代表标准输入时,不用自己写两套分支,argparse.FileType 就够了:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("input", type=argparse.FileType("r", encoding="utf-8"))
args = parser.parse_args()

for line in args.input:
    print(line.rstrip())

保存为 echo_lines.py 后,这两种调用都能跑:

python echo_lines.py notes.txt
printf 'one\ntwo\n' | python echo_lines.py -

环境是 Python 3.9+。文件不存在或无法打开时,参数解析会直接给出错误;- 则会读 stdin。做命令行小工具时挺省事。

这个写法很适合小工具。补一个资源管理的边界:普通文件是 FileType 在解析参数时就打开的,后面如果还有参数校验或业务报错,关闭时机不如显式 with 直观;而 - 返回的是 sys.stdin,也不该由业务代码关闭。

如果命令后面还有比较复杂的处理,我会保留路径字符串,再用 contextlib.nullcontext(sys.stdin) 配合 Path.open(),这样文件生命周期更清楚。简单的一次性脚本用 FileType 就很省代码。

Ryan小满Lv1#1

这个写法很适合小工具。补一个资源管理的边界:普通文件是 FileType 在解析参数时就打开的,后面如果还有参数校验或业务报错,关闭时机不如显式 with 直观;而 - 返回的是 sys.stdin,也不该由业务代码关闭。

如果命令后面还有比较复杂的处理,我会保留路径字符串,再用 contextlib.nullcontext(sys.stdin) 配合 Path.open(),这样文件生命周期更清楚。简单的一次性脚本用 FileType 就很省代码。

这个边界补得很实用。再提醒一点:如果后面把输入交给多个处理阶段,最好别在 finally 里无条件关闭句柄,因为 - 对应的是 sys.stdin。简单脚本用 FileType 很省事,复杂命令则把路径留到业务层,用 with 管普通文件会更稳。