在Python脚本中解析命令行参数(getopt问题)

4

有人能发现以下脚本为何不打印传递的参数吗?

import sys, getopt

def usage():
    print 'Unknown arguments'

def main(argv):
    try:
        opts, args = getopt.getopt(argv,'fdmse:d',['files=','data-source=','mode=','start','end'])

    except getopt.GetoptError:
        usage()
        sys.exit(999)

    for opt, arg in opts:
        # print opt,arg 
        if opt in('-f','--files'):
            print 'files: ', arg  #

if __name__ == "__main__":
    main(sys.argv[1:])

当我在命令行上运行脚本并传递参数-f=dummy.csv时,似乎会调用usage()函数 - 为什么?
顺便说一下,我觉得程序流程的逻辑有点奇怪(我从这里复制了它)。通常,我认为逻辑将在try分支中实现,然后在此之后才是异常处理程序。
这是否(如上面的代码所粘贴的)是编写try/catch块的“Pythonic”方式?

提示:打印GetoptError的值。except getopt.GetoptError, e: print e。如果不打印错误消息,你永远不会知道出了什么问题。稍后,在调试完成后,您可以注释掉print语句。现在,请打印异常。 - S.Lott
我按照你的建议尝试了一下,但是出现了错误信息:“未识别选项 -=”。我修正了传递参数的方式。现在,如果我传递--files=foobar.csv,脚本可以正常工作,但是当我传递-f foobar.csv时,该值被打印为空字符串??? - skyeagle
虽然这与主题不完全相关,但使用argparse会更好,更用户友好(因为您是新手)。http://docs.python.org/library/argparse.html#module-argparse - Tushar Tyagi
@tushatyagi:可能应该提到我正在使用Python 2.6。如果我没记错的话,argparse是2.7版本才有的新功能。不过我会再次确认的。 - skyeagle
“-f”选项不需要参数值。您是否对如何呈现getopt选项感到困惑?需要使用“f:”来使“-f”选项查找一个值。您真正的问题是什么?我无法理解所有的评论。请更新此内容以使其具有逻辑意义。 - S.Lott
optparse 有其缺点,但它也是一个很好的模块。 - tokland
3个回答

2

你有得到答案吗?

调试 Python 异常的一种方法是将代码移出 try 块(或者为了调试而暂时复制代码)。这样你就可以获得完整的跟踪信息。

当然,另一种方法是缩小测试用例。在这里,我将问题缩小到三行,并尝试了 @s.lott 提示的解决方案(在 getopts 调用中使用 'f:'),并且最后展示了使用一些不同的测试数据进行调用的行为:

$ cat x1.py
import sys, getopt
opts, args = getopt.getopt(sys.argv[1:],'fdmse:d',['files=','data-source=','mode=','start','end'])
print "opts=", opts, "args=", args

$ python x1.py -f=dummy.csv argblah
Traceback (most recent call last):
  File "x1.py", line 2, in <module>
    opts, args = getopt.getopt(sys.argv[1:],'fdmse:d',['files=','data-source=','mode=','start','end'])
  File "/usr/lib/python2.6/getopt.py", line 91, in getopt
    opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
  File "/usr/lib/python2.6/getopt.py", line 191, in do_shorts
    if short_has_arg(opt, shortopts):
  File "/usr/lib/python2.6/getopt.py", line 207, in short_has_arg
    raise GetoptError('option -%s not recognized' % opt, opt)
getopt.GetoptError: option -= not recognized

$ sed 's/fdm/f:dm/' <x1.py >x2.py

$ diff x1.py x2.py
2c2
< opts, args = getopt.getopt(sys.argv[1:],'fdmse:d',['files=','data-source=','mode=','start','end'])
---
> opts, args = getopt.getopt(sys.argv[1:],'f:dmse:d',['files=','data-source=','mode=','start','end'])

$ python x2.py -f=dummy.csv argblah
opts= [('-f', '=dummy.csv')] args= ['argblah']

$ python x1.py -f dummy.csv argblah
opts= [('-f', '')] args= ['dummy.csv', 'argblah']

1
通常来说,我会认为逻辑将在 try 分支中实现。
“通常”?这是什么意思?
程序应该做什么?哪些异常是有意义的?程序对异常的响应是什么?
没有“通常”。就像没有正常的赋值语句或正常的函数定义一样。
你的程序应该根据需要实现合理的目标。没有“通常”的说法。

通常情况下,就像在C++、C#和Java中一样。在所有这些语言中,逻辑都在try块中,异常(如果被抛出)稍后处理。我的背景(你可能已经猜到了)主要是C++(和其他提到的语言),所以我基本上习惯了用我描述的方式处理异常——如果这不是Pythonic的做法,那也没关系——我只是想知道处理异常的Pythonic方式。 - skyeagle
请不要一直说“通常”。这是您基于事物应该做什么以及可能发生的异常以及对异常的响应而做出的设计决策。您不能随意添加try块。也没有所谓的“通常”。您必须认真思考和设计try块。即使在C++中也是如此。 - S.Lott
冒着偏离原问题的风险,我要说的是 - 在我提到的所有语言中,编写try/catch[/finally]块只有一种方法 - 这就是我使用“通常”这个词的原因。我从未见过一个try/catch语句,在这个逻辑之前出现异常处理程序可能会抛出异常。仅此而已... - skyeagle
@ekyeagleпјҡд»Җд№ҲпјҹдҪ жҳҜиў«е“ӘдёӘиҜӯеҸҘеҸҜиғҪеј•еҸ‘getopt.GetoptErrorжүҖеӣ°жғ‘дәҶеҗ—пјҹиҝҷжҳҜдҪ зңҹжӯЈзҡ„й—®йўҳеҗ—пјҹеҰӮжһңжҳҜпјҢиҜ·жӣҙж–°дҪ зҡ„й—®йўҳгҖӮ并确дҝқеңЁжӣҙж–°дёӯжү“еҚ°еј•еҸ‘зҡ„ејӮеёёгҖӮ - S.Lott
我理解你的观点。异常处理器在可能抛出异常的语句之后。我的错。现在一切都好了。 - skyeagle

0

使用 argparse 而不是 getopt 进行导入和使用。它更易于使用,并且几乎所有需要从命令行运行的功能都已内置其中。

一个例子:

    parser = argparse.ArgumentParser(
        description='Description of what the module does when run.')
    parser.add_argument("-o", "--output", help='Path of log file.')
    args = parser.parse_args()

就这么简单。当然,你需要在文件顶部导入argparse才能让它工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接