python解析命令列

發表於2016-12-13

getopt:和C中的getopt()等價。
optparse:2.7後已不推薦使用。
argparse:基於optparse的新庫。
docopt:根據文件描述,自動生成。另一份參考文件:docopt

更詳細的內容可參考上述文件。

getopt


若對C的getopt()函式不熟悉,或者傾向於使用較少的程式碼,或者需要對幫助資訊和錯誤資訊有更高要求,以上情況優先使用argparse
該模組主要提供兩個函式,以及一個異常判斷。

getopt.getopt(args, options[, long_options])
args是需要解析的列表,通常情況下,是sys.argv[1:]
options是解析列表時使用的正規化,如果某一項(單個字母)後面需要引數,則在此項後新增':'
long_options必須是字串列表,字串開頭的'--'不能包括,若某一項後面需要引數,則在此項後新增'='。可選型的引數是不支援long_options的。是根據輸入是否是字串列表中唯一一項的字首來匹配的。
返回值分為兩個部分。第一部分是(option, value)格式的列表,第二部分是解析完後剩餘部分。

getopt.gnu_getopt(args, options[, long_options])
作用同上。
區別在於getopt()一旦遇到未配置的選項,就會停止解析。而此函式使用GNU規則,即已配置的選項和未配置的選項是可以混雜的。

exception getopt.GetoptError
當一個無法識別的選項,或者一個必須要有引數傳入的選項未傳入引數時,丟擲此異常。
exception getopt.error
GetoptError的舊名,為了向後相容而保留。

具體例項可參考文件,不推薦使用。

argparse


一個簡單例子:

argparse中,最常用的就是上述三部分了:建立一個ArgumentParser物件;使用add_argument()方法來為建立的ArgumentParser物件新增argument的解析規則;最後呼叫parse_args()來解析傳入的內容,依據的是第二步制定的規則,生成的是一個Namespace物件,若未傳引數給parse_args(),那麼預設從sys.argv來獲取命令列入參。

class argparse.ArgumentParser(prog=None, usage=None, description=None, epilog=None, parents=[],formatter_class=argparse.HelpFormatter, prefix_chars=’-‘, fromfile_prefix_chars=None, argument_default=None,conflict_handler=’error’, add_help=True)
prog:程式的名字,預設是argv[0]。若設定,則在幫助資訊中,可以使用%(prog)s來作為格式化的引用(修改一處全域性受用)。
usage:幫助資訊的usage欄位,描述程式的各種用法,預設情況下會依據add_argument()來自動生成。
description:一個簡單描述程式主要幹啥以及怎麼用的字元段。位於usage字元段和optional arguments字元段之間。
epilogoptional arguments字元段之後的字元段。
parents:繼承的父parser,為了避免一些公共的內容重複定義,父parser在初始化時會設定add_help=False,這是為了防止出現父與子parser的-h衝突而丟擲異常。
formatter_class:對於help輸出進行格式化,除了輸出的樣式外,如果設定為ArgumentDefaultsHelpFormatter,則會自動在help輸出中新增已定義的default值。
prefix_chars:options前的字元,預設為’-‘,可以新增其他字元,如’-+’,但是如果沒有包括’-‘,那麼對應的option如’-h’就無法解析。
fromfile_prefix_chars:有時會使用檔案給parse_args()傳入引數,為了能夠識別檔案字串,如”demo.txt”,需要設定此值,如”@”,那麼所有以此字元為開頭的字串都被當作是檔案,所以傳給parse_args()的引數應該是@demo.txt。在該檔案中,一行只能有一個引數。如檔案中的'-f\nbar'會被解析成['-f','bar']
argument_default:一般情況下,預設值使用add_argument()來新增,或者使用set_defaults()設定一些鍵值對來新增。剩下一種情況就是設定此項(此處沒看明白是咋回事)。
conflict_handler:解決在add_argument()階段有衝突的option的依據策略,預設為error即丟擲異常。一般情況下遇到衝突是丟擲異常即可,但是如果設定了parents,那麼需要重寫父parser中的規則的時候,就需要將此項設定為resolve,但是重寫是精確匹配的,如老規則定義了-h/--help,重寫了-h,那麼--help還是老規則。
add_help:是否新增-h/--helpoption,預設為True。為False時,是要做parent(其實可以設定子Parser重寫)。預設是-h/--help,若prefix_chars中沒有包含’-‘,那麼就以其中第一個字元作為代替。

ArgumentParser.add_argument(name or flags…[, action][, nargs][, const][, default][, type][, choices][, required][, help][,metavar][, dest])
name or flags:是位置引數,則需要傳入名字;要是可選引數,則需要進行定義,如’-f’,’–foo’。
action:定義傳入的引數如何處理。

  • action='store',預設取值,儲存傳入引數。
  • action='store_const',需要新增const,意味著該argument的值不從命令列輸入,而是取const的值。
  • action='store_true' or action='store_false''store_const'的特殊情形,意味著const的值為TrueFalse
  • action='append',表示傳入的值會作為一個列表的一項,意味著option可以在命令列中多次出現。
  • action='append_const',傳入列表的項由const定義,通常用在需要多個argument將值傳入一個列表中的場景。
  • action='count',輸出argument出現的次數。
  • action='help',已預設新增。
  • action='version',需要定義version,使用時輸出版本資訊並退出。
  • 自定義,通過定義一個argparse.Action子類來實現。實際上,上面的這些可選項都是通過這種形式定義的。

nargs:通常一個選項後跟一個引數,通過設定此項可以實現不同情況。

  • nargs=N,一個選項後可以跟多個引數(action='append'時,依然是一個選項後跟一個引數,只不過選項可以多次出現),引數的個數必須為N的值,這些引數會生成一個列表,當nargs=1時,會生成一個長度為1的列表。
  • nargs=?,如果沒有在命令列中出現對應的項,則給對應的項賦值為default。特殊的是,對於可選項,如果命令列中出現了此可選項,但是之後沒有跟隨賦值引數,則此時給此可選項並不是賦值default的值,而是賦值const的值。
  • nargs=*,和N類似,但是沒有規定列表長度。
  • nargs=+,和*類似,但是給對應的項當沒有傳入引數時,會報錯error: too few arguments
  • nargs=argparse.REMAINDER,所有剩餘的引數,均轉化為一個列表賦值給此項,通常用此方法來將剩餘的引數傳入另一個parser進行解析。

如果nargs沒有定義,則可傳入引數的數量由action決定,通常情況下為一個,並且不會生成長度為一的列表。
const,一種是定義action='store_const'action='append_const'時使用。一種是定義nargs='?'時,可選項出現在命令列中,但之後並沒有跟隨賦值的引數,作為預設值傳給此可選項。
default:預設值。
如果是一個字串,那麼Parser解析的時候會將它作為命令列傳入值,使用type的值來進行轉換型別,但是如果不是的話,就會使用定義的值而不進行型別轉換。
如果設定了nargs='?'nargs='*',那麼當沒有引數賦值給該項時,會使用default定義的值。
default=argparse.SUPPRESS時,則表示命令列中未出現某一項時,不會對它進行預設賦值。
type:用於型別檢查和型別轉換。
使用FileType可簡化對檔案的操作。
還可以自定義函式,輸入是一個字串,輸出是轉換後的字串。
當設定choices的時,型別檢查會變得容易,因為只需要在一個範圍內比較即可。
choices:給定了取值範圍,超出會報錯。
type也有定義時,會先使用type進行型別檢查,所以choices中的取值必須符合type的定義,否則在parse_args()時會報錯。
任何支援in操作符的均可作為choices的賦值,所以字典,列表,集合,等等其他容器均都支援。
required:預設情況下,可選項(前面有'-')被認為並不一定需要出現在命令列引數中,但是如果設定了required=True的話,則必須出現。此類設定違揹人的常識,應避免使用。
help:幫助資訊。
之前提到的%(prog)s可用於此處程式名的格式化,此外,還有%(default)s格式化default的值,%(type)s格式化type的值。
設定為argparse.SUPPRESS可不顯示幫助資訊。
metavar:在Parser生成幫助資訊時,需要有字元代表需要傳入的值。(這一段和dest相同,使用的就是dest的值)如果是位置引數,則用它本身代替;如果是可選引數,則使用它的大寫來代替。使用metavar可替換預設的字元。
dest:大部分的選項都需要通過命令列來給其賦值,這些值的名字通過dest來定義,預設的規則如同metavar中所述。

ArgumentParser.parse_args(args=None, namespace=None)
args轉換為namespace物件的一個值。預設情況下,sys.argv賦值給args,一個空的Namespace物件會被建立。
解析時,會對傳入的引數進行檢查,若不符合要求就會報錯。
一般情況下,會自動判斷傳入的值到底是一個可選引數,還是一個負數(都用’-‘開頭)。但有時位置引數的值必須是一個’-‘開頭的值,如'-f',那麼使用parser.parse_args(['--', '-f'])'--'代表後續的所有傳入值都需要看做是位置引數。
parse_args()會返回填充好的Namespace物件,若要將它變為字典,可使用Python自帶的vars()

也可不使用Namespace,而是傳入一個提前生成的物件。

其他
ArgumentParser.add_subparsers([title][, description][, prog][, parser_class][, action][, option_string][, dest][, help][, metavar])用來定義子Parser。
class argparse.FileType(mode=’r’, bufsize=None)用來給add_argument()中的type引數賦值。
ArgumentParser.add_argument_group(title=None, description=None)定義一個組。
ArgumentParser.add_mutually_exclusive_group(required=False)定義組中只能有一個選項出現。
ArgumentParser.set_defaults(kwargs)設定預設值。
ArgumentParser.get_default(dest)獲取預設值。
ArgumentParser.print_usage(file=None)
ArgumentParser.print_help(file=None)
ArgumentParser.format_usage()
ArgumentParser.format_help()以上四種都是幫助資訊相關的。
ArgumentParser.parse_known_args(args=None, namespace=None)只解析知道的部分。
ArgumentParser.convert_arg_line_to_args(arg_line)
ArgumentParser.exit(status=0, message=None)
ArgumentParser.error(message)

docopt


理念是好的幫助資訊應該正好包含生成命令列解析器所需要的全部資訊。

docopt.docopt(doc, argv=None, help=True, version=None, options_first=False)
doc引數可以是一個模組的docstring(__doc__),或者是其他符合格式的幫助資訊。
argv預設情況下使用sys.argv[1:],或者可以使用一個字串。
help預設為True,當輸入-h--help後(需提前設定好),解析器能夠自動生成對應的幫助文字。需要手動管理的可設定為False。
version版本資訊,是可選引數。
options_first預設為False。當設定為True時,不允許可選引數和位置引數進行混合,即在出現的第一個位置引數之後的所有引數,均被當作是位置引數,這是為了和POSIX保持相容。
返回值是一個字典,key是option,value就是對應輸入的引數。

幫助資訊格式分為兩部分,Usage patternOption descriptions。只有符合格式的字串才會被識別並解析,其餘字串會被忽略。
Usage pattern
舉例:

Usage pattern是doc的子字串。開始於usage:(大小寫不敏感),結束於一行空行。
位於usage:之後的第一個單詞是程式的名字,可以重複多次進行不同pattern的定義。

每個pattern都由以下部分組成。
一個是<arguments>, ARGUMENTS格式的定義,要麼是用尖括號包括,要麼是大寫。
還有一個是-options,必須要以“-”開頭,可以堆疊好幾個option,如-oiv等價於-o -i -v。option是可以有引數的,如-f FILE-f=FILE-fFILE,都是等價的。其他一些選擇在下方的option descriptions中進行具體定義。
最後一個是commands,除了以上提及的兩種情況,剩下的格式,再加上兩個特殊的命令:單個的“-”和雙個的“-”,這些都歸屬於此。

commands中,[ ]表示可選。( )表示必須存在,所有沒有用[ ]包裹的字串都預設被( )包裹。
|是管道符,若某一個選項必須存在,則將他們用( )包裹,如my_program.py(--clockwise | --counter-clockwise) TIME,若都是可選項,則用[ ]包裹,如my_program.py[--left | --right]
表示有一個或多個元素,如my_program.py FILE ...表示一個或多個FILE可接受,而my_program.py [FILE ...]表示0個或多個FILE可接受。
[options](大小寫敏感)是任意options的縮寫。可以使用它來定義該pattern可以使用任何在下方Option descriptions中定義的option。
[–]是為了分隔可選引數和位置引數,如Usage: my_program [options] [--] <file>...,其中中括號可去除,變為必填項。
[-]若出現,則表示程式需要使用stdin,禁止使用file。而單個的則是一個普通字元。

如果在usage pattern中重複出現同名位置引數多次,或者同名有引數的可選引數多次,那麼傳入的值會被解析成一個列表。如Usage: my_program.py <file> <file> --path=<path>...的結果會是args['<file>'] == ['file1', 'file2']以及args['--path'] == ['./here', './there']

Option descriptions
舉例:

此部分位於usage pattern下方,它可以有三方面作用:定義同義的短option和長option;某個option是否需要傳入引數;某個option是否有預設引數。
規則如下:
每行定義一個option,必須以一個或兩個“-”開頭。
為定義需要傳入的引數,可以在對應的option後方加上一個字串,兩者以空格或“=”相隔,兩者都是可行的,但推薦只使用一種風格。

使用兩個空格來將option本身與它的描述想分隔(按照示例應該是至少兩個空格)。

在option對應的描述後方進行預設引數的設定,格式是[default:]

若某option是可重複的,則預設引數會以空格作為分割符進行分割,生成一個字串列表。否則,作為一個字串整體。

docopt不適用於大型多層次的命令列引數解析(例如git,但是官方給出了一個example)。同時它沒有對資料的校驗功能,無法向使用者報錯。
使用try docopt可進行docopt線上命令列引數解析。

舉例


例題是:知道創宇面試題
使用argparse

輸出:

體會就是功能很強大,實現很方便。所以我傾向於使用它。

使用docopt

輸出:

可見,如果選項不需要引數,則-h和–version這種沒有設定的會賦值為False,而–testself會賦值為True;對於需要引數的選項,如果沒有設定,如–key,會被賦值為None,而有設定並且給出引數的,會賦值對應的引數,如–dbfile。
那麼,對於有設定但是沒有給出引數的呢,並不會丟擲異常報錯,而是把Usage部分(有時加上Options部分)輸出,真是太不友好了,debug的時候真是坑爹啊。

另外,使用docopt時候需要注意__doc__的用法,如果是寫函式的__doc__,則在函式名之下開始寫,而一個模組的則必須在開頭寫,也就是在import之前寫,否則__doc__ == None

相關文章