2、命令行参数解析:使用sys.argv、argparse库、getopt库处理用户输入参数

写脚本的时候,最烦什么?我猜是每次都要改代码里的硬编码路径、IP地址、端口号。说实话,我刚入行那会儿也这么干——改一次跑一次,改到后来自己都忘了改过什么。后来我学乖了,开始用命令行参数。

命令行参数解析,说白了就是让用户在执行脚本时,通过命令行告诉脚本「我要干什么」。比如 python deploy.py --env prod --port 8080,而不是打开脚本改代码。今天我就把三种主流方法掰开揉碎讲清楚。

核心知识点一览:

  • sys.argv:最原始、最灵活,适合简单场景
  • argparse:Python官方推荐,功能强大,适合复杂参数
  • getopt:兼容C语言风格,适合老项目迁移
命令行参数解析三种方案对比 命令行参数解析 sys.argv 原始、灵活、无依赖 argparse 官方推荐、功能全面 getopt C风格、兼容老项目 适用场景:简单脚本 → 复杂工具 → 遗留系统迁移 推荐指数:★★☆☆☆ → ★★★★★ → ★★★☆☆

2.1 sys.argv:最朴素的参数获取方式

sys.argv 是什么?就是一个列表。脚本名是 sys.argv[0],后面依次是用户传的参数。举个例子:

# demo_argv.py
import sys

print(f"脚本名:{sys.argv[0]}")
print(f"参数个数:{len(sys.argv) - 1}")
for i, arg in enumerate(sys.argv[1:], 1):
    print(f"第{i}个参数:{arg}")

执行一下看看:

$ python demo_argv.py deploy prod 8080
脚本名:demo_argv.py
参数个数:3
第1个参数:deploy
第2个参数:prod
第3个参数:8080

简单吧?但问题也明显——参数顺序必须固定。你想想看,如果用户忘了顺序,把 prod 和 8080 写反了,脚本照样跑,结果全错。我在项目中遇到过这种坑,排查了半天才发现是参数顺序搞反了。

注意:sys.argv 拿到的全是字符串。如果你需要数字,记得自己转类型。比如 port = int(sys.argv[3]),不然 8080 就是个字符串,做加法会变成拼接。

什么时候用 sys.argv?我个人习惯是写一次性脚本、临时测试、或者参数不超过两三个的时候。简单粗暴,不用引入任何库。

2.2 argparse:官方推荐的参数解析利器

如果你要写一个正经的工具,别犹豫,直接用 argparse。它从 Python 2.7 开始就是标准库了,功能非常全面。

先看一个完整的例子:

# deploy.py
import argparse

def main():
    parser = argparse.ArgumentParser(
        description='自动化部署工具 v2.0',
        epilog='示例:python deploy.py --env prod --port 8080 --verbose'
    )
    
    # 位置参数(必填)
    parser.add_argument('action', choices=['deploy', 'rollback', 'status'],
                       help='操作类型:deploy/rollback/status')
    
    # 可选参数
    parser.add_argument('--env', '-e', default='dev',
                       choices=['dev', 'test', 'prod'],
                       help='部署环境(默认:dev)')
    parser.add_argument('--port', '-p', type=int, default=8000,
                       help='服务端口(默认:8000)')
    parser.add_argument('--verbose', '-v', action='store_true',
                       help='开启详细日志')
    parser.add_argument('--config', '-c',
                       help='配置文件路径')
    
    args = parser.parse_args()
    
    print(f"操作:{args.action}")
    print(f"环境:{args.env}")
    print(f"端口:{args.port}")
    print(f"详细模式:{args.verbose}")
    if args.config:
        print(f"配置文件:{args.config}")

if __name__ == '__main__':
    main()

执行效果:

$ python deploy.py deploy --env prod --port 8080 -v
操作:deploy
环境:prod
端口:8080
详细模式:True

$ python deploy.py --help
usage: deploy.py [-h] [--env {dev,test,prod}] [--port PORT] [--verbose] 
                 [--config CONFIG]
                 {deploy,rollback,status}

自动化部署工具 v2.0

positional arguments:
  {deploy,rollback,status}
                        操作类型:deploy/rollback/status

optional arguments:
  -h, --help            show this help message and exit
  --env {dev,test,prod}, -e {dev,test,prod}
                        部署环境(默认:dev)
  --port PORT, -p PORT  服务端口(默认:8000)
  --verbose, -v         开启详细日志
  --config CONFIG, -c CONFIG
                        配置文件路径

示例:python deploy.py --env prod --port 8080 --verbose

看到没?自动生成了帮助信息,参数类型自动转换,还有 choices 做合法性校验。我曾经在给客户写运维工具时,用 argparse 做了几十个参数,嵌套了子命令,用户用起来非常顺手。

我的小技巧:

  • 短选项(如 -p)给常用参数,长选项(如 --port)给全称
  • 用 type=int 自动转数字,省得自己写 int()
  • 用 choices 限制取值范围,用户输错了直接报错
  • 用 action='store_true' 做开关参数,传了就 True,不传就 False

2.3 getopt:老牌选手,兼容C风格

说实话,我现在很少用 getopt 了。但如果你要维护老项目,或者团队里有人习惯 C 语言的 getopt 风格,那还是得会。

getopt 的用法和 argparse 不太一样,它需要你手动处理参数列表:

# old_style.py
import sys
import getopt

def main():
    # 短选项格式:'e:p:v' 表示 -e 带值、-p 带值、-v 不带值
    # 长选项格式:['env=', 'port=', 'verbose'] 表示 --env 带值、--port 带值、--verbose 不带值
    short_opts = 'e:p:v'
    long_opts = ['env=', 'port=', 'verbose']
    
    try:
        opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
    except getopt.GetoptError as err:
        print(f"参数错误:{err}")
        sys.exit(1)
    
    env = 'dev'
    port = 8000
    verbose = False
    
    for opt, val in opts:
        if opt in ('-e', '--env'):
            env = val
        elif opt in ('-p', '--port'):
            port = int(val)
        elif opt in ('-v', '--verbose'):
            verbose = True
    
    print(f"环境:{env}")
    print(f"端口:{port}")
    print(f"详细模式:{verbose}")
    
    # args 里是剩余的位置参数
    if args:
        print(f"其他参数:{args}")

if __name__ == '__main__':
    main()

执行效果:

$ python old_style.py -e prod -p 8080 -v
环境:prod
端口:8080
详细模式:True

注意:getopt 不会自动生成帮助信息,也不会做类型转换。你需要自己写 int(val),自己处理异常。说白了,它就是个「半自动」的工具。

2.4 三种方案对比

特性 sys.argv argparse getopt
学习成本 极低 中等 低
自动帮助信息 ❌ ✅ ❌
类型自动转换 ❌ ✅ ❌
参数校验 ❌ ✅ ❌
子命令支持 ❌ ✅ ❌
代码量 少 中等 中等
适用场景 临时脚本 正式工具 老项目维护

2.5 实战建议

说了这么多,到底怎么选?我个人的经验是这样的:

  • 写一次性脚本:用 sys.argv,三五行搞定,别折腾
  • 写团队工具:用 argparse,自动生成帮助文档,同事用起来也方便
  • 维护老项目:用 getopt,保持风格一致,别没事找事去重构

另外,有个小细节要注意——参数解析的代码最好单独写一个函数,别和业务逻辑混在一起。我见过有人把参数解析写在 main 函数里,结果想复用脚本逻辑时,还得带着一堆参数解析代码,特别别扭。

避坑指南:

我曾经在写一个批量部署脚本时,用 sys.argv 传了 8 个参数。结果上线那天,运维同事传错了顺序,把测试环境的配置部署到了生产环境……嗯,从那以后,但凡超过 3 个参数,我必用 argparse。

好了,命令行参数解析就讲到这里。记住一句话:参数解析是脚本的「门面」,用户第一眼看到的就是它。做得好了,用户觉得你专业;做得差了,用户连用都不想用。

可可精品资料库