getopt 模块使用注意事项(踩坑日记)

前言

最近和我们学校的IT社长写一个网页下载工具,今天刚更新命令行参数功能,踩了坑getopt模块中的一个坑,main.py文件完整源码如下

# coding=utf-8

import sys
import getopt
from DownloadWebPage import DownloadWebPage


# url = 'https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_8912090858788091154%22%7D&n_type=0&p_from=1'

def _usage() -> str:
    print("""
    Download WebPage Main Program

    -h,--help Display help.
    -e,--encoding Set encoding for website files recoding.
    -p,--path Set webpage all files main path.(Default on ./data)
    -s,--static Set webpage static files path.(Default on {path}/static)
    -t,--timeout Set request timeout.(Default 30sec)

    Examples:
        python main.py http://www.google.com
        python main.py http://www.google.com -t 60
        python main.py http://www.google.com --path ./website
        python main.py http://www.google.com --path ./website --static /res
    
    """)


def main(argv):
    try:
        opts, args = getopt.getopt(argv, "he:t:p:s:", [
                                   "encoding=", "timeout=", "path=", "static=", ])
    except getopt.GetoptError as err:
        print(err)
        _usage()
        sys.exit()
    if len(args) < 1:
        print('[!!!] No url!')
        sys.exit()
        # assert False
    else:
        url = args[0]
        if len(opts) < 1:
            path = static = encoding = timeout = None
        for opt, value in opts:
            if opt in ('-h', '--help'):
                _usage()
                sys.exit(0)
            path = value if opt in ('-p', '--path') else None
            static = value if opt in ('-s', '--static') else None
            encoding = value if opt in ('-e', '--encoding') else None
            timeout = int(value) if opt in ('-t', '--timeout') else None
        dwp = DownloadWebPage(url, path, static, encoding, timeout)
        dwp.save()


# 当为主程序入口
if __name__ == '__main__':
    if len(sys.argv) < 2:
        _usage()
        sys.exit()
    main(sys.argv[1:])

以上逻辑看起来很完美(这里不得不佩服社长,代码敲的比我六)。

更新完后第一时间测试下
getopt 模块使用注意事项(踩坑日记)_第1张图片

编码问题,意料之中

试下刚更新的命令行参数功能,加个 utf-8 完成测试
getopt 模块使用注意事项(踩坑日记)_第2张图片
getopt 模块使用注意事项(踩坑日记)_第3张图片

这次结果出乎意料,加了utf-8一样报错,开始各种测试(为什么不怀疑是解码的问题?因为之前的编码问题已经解决,加utf-8是可以解决的),最后发现是getopt模块的问题

开始测试getopta模块

getopt 模块使用注意事项(踩坑日记)_第4张图片

嗯,为了不影响开发分支,单独开一个文件 test.py
源码如下

import getopt
import sys

def _usage():
    print(
    """
    -h,--help   Help information
    -d,--delete Delete file
    -a,--add    Add file
    """
    )

def main():
    opts,args = getopt.getopt(sys.argv[1:], "hd:a:", ["help","delete=","add="])
    print("opts:",opts)
    print("srgs:",args)

if __name__ == "__main__":
    main()

大概测试了一遍,发现是参数顺序问题

getopt.getopt()会同时返回两个列表
一个列表由参数的 "选项/值" 所组成的元组组成例如-d是删除文件按的一个选项 python test.py -d test.txt -a new.txt ,处理后会返回 [('-d','test.txt'), ('-a','new.txt')]
另一个列表由无选项参数组成,例如python main.py file.txt file2.txt,处理后会返回 ['file.txt', 'file2.txt']

小提示:

在以下测试中 getopt.getopt()
返回的带选项的参数列表的名称为 opts
返回的不带选项的参数列表为 args

如果把带选项的参数放到开头,无选项参数放到末尾,getopt运行正确


但要是反过来就要命了
从上方图片可以看到 ,getopt把 -e delete_file.txt 作为不带选项的参数来处理,这并不是我们期待的结果,说了这么多,没有解决方法不都是废话么?

解决方法

解决方法很简单,使用getopt.gnu_getopt()函数就行了。

看文档
getopt 模块使用注意事项(踩坑日记)_第5张图片

这个函数像 getopt() 一样工作,除了默认使用GNU样式扫描模式。这意味着选项和非选项参数可以混合。一旦遇到非选项参数,getopt() 函数就停止处理选项。
如果选项字符串的第一个字符是“+”,或者如果设置了环境变量POSIXLY_CORRECT,那么一旦遇到非选项参数,选项处理就会停止。

gun_getopt()允许参数混合,嗯。。。。光速修改替换getopt()gun_getopt()

import getopt
import sys


def _usage():
    print(
    """
    -h,--help   Help information
    -d,--delete Delete file
    -a,--add    Add file
    """
    )

def main():
    # getopt() -> gun_getopt()
    opts,args = getopt.gnu_getopt(sys.argv[1:], "hd:a:", ["help","delete=","add="])
    print("opts:",opts)
    print("srgs:",args)


if __name__ == "__main__":
    main()
修改后的运行结果

哦~~ 完美

总结

又是一起不熟悉模块而熬夜改BUG的血案
getopt 模块使用注意事项(踩坑日记)_第6张图片

你可能感兴趣的:(getopt 模块使用注意事项(踩坑日记))