利用Click和argparse給你Python程序構建一個優(yōu)雅的命令行界面
我們知道在Linux下有優(yōu)雅的shell終端命令行界面,shell腳本都可以優(yōu)雅用命令行的方式來運行。而且shell也再帶優(yōu)化命令行參數(shù)解析的bash內(nèi)部命令getopts和大多數(shù)發(fā)行版附帶的外部命令getops。Perl語言也有Getopt::XX系列模塊來實現(xiàn)類似功能;Golang也有flag標準庫以及更加強大的第三庫cobra。

說了這么多你肯定要問,那么Python呢?Python不像shell,perl一樣有便捷強大的oneline腳本,雖然Python有強大的web界面下的腳本執(zhí)行環(huán)境Jupyter Note,但是對終端命令行下的執(zhí)行也是我們要追求的目標。本文蟲蟲就給大家介紹如何在Python下實現(xiàn)優(yōu)雅的終端命令行界面CLI,涉及的模塊有Click和argparse。
Click
Python開始的時候也模仿bansh和其他語言有getopt,optparse等模塊來做CLI,后來推出argparser模塊后getopt,optparse等都已經(jīng)被廢棄。在介紹argparser模塊之前我們先來介紹click。
首先最簡單的方式是使用click。我們先來看下python 3中的print()函數(shù),注意該print()強制必須以帶括號方式, 這是和python2比較大的區(qū)別。其定義如下:
- print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
可以看到該函數(shù)可以支持各種規(guī)范格式的輸出,而不單單一個打印輸出了。
比如,通過可以自定義打印的結束字符,默認是"\n"。
- print('hello,chongchong! ', end='\r')
可以通過sep自定義分割符,而不是默認的空格。
- print('hello', ' chongchong!', sep=',')
通過file來重定向輸出到文件,而不是stdout的默認標準輸出。
- print(' hello,chongchong!', file=open('chongchong.out', 'w'))
我們可以利用click在命令行中實現(xiàn)對這些參數(shù)的輸入和解析,實現(xiàn)一個簡單的打印命令行界面。廢話少說直接上代碼:
- #!/usr/bin/env python3
- import click
- import sys
- @click.command()
- @click.argument('args', nargs=-1)
- @click.option('-s', '--sep', default=' ', help='分隔符,默認為空')
- @click.option('-e', '--end', default='\n', help='結尾字符,默認為 "\\n"')
- @click.option('-o', '--out', default=None, help='輸出文件,默認為stdout)')
- def print_prog(args, sep, end, out):
- '''打印輸出結果。
- '''
- print(*args, sep=sep, end=end, file=open(out, 'w') if out else sys.stdout)
- if __name__ == '__main__':
- print_prog()
腳本很簡單,就是獲取輸入的參數(shù),并將其傳遞給print()函數(shù)執(zhí)行。執(zhí)行如下:
打印文本
- python print.py hello,chongchong!
顯示幫助
- python print.py –help

設置風格符號
- python print.py -s %% hello chongchong!
重定向到文件
- python print.py -o chongchong.out -s %% hello chongchong!

通過幾行代碼就實現(xiàn)了一個很棒的交互界面!
當然你可以依次為基礎增加更多的內(nèi)容,讓它更實用這是一個演示,向您展示創(chuàng)建自己的CLI是多么容易。你不再需要編寫普通的python腳本文件了。更多功能和高級特性可以參考click官方文檔:

更多的基于click的項目和模塊也可以通過其github倉clone和fork使用,你也可以給該項目添加自己的貢獻。
具體地址為:github:/click-contrib/。
argparse
我們首先從官方的數(shù)字計算程序小例子來開始:
- import argparse
- parser = argparse.ArgumentParser(description='Process some integers.')
- parser.add_argument('integers', metavar='N', type=int, nargs='+',
- help='an integer for the accumulator')
- parser.add_argument('--sum', dest='accumulate', action='store_const',
- const=sum, default=max,
- help='sum the integers (default: find the max)')
- args = parser.parse_args()
- print args.accumulate(args.integers)
代碼解釋:
首先,通過argparse.ArgumentParser()創(chuàng)建一個解析對象;
接著,給對象添加兩個參數(shù),用add_argument()方法。比如本例中是一個type為一個或者一列整數(shù);還有一個目標為accumulate屬性用來選擇兩個函數(shù)sum()和max()求和和求最大值,默認為求最大值。
然后,通過parse_args()函數(shù)解析對象的參數(shù),并把各個參數(shù)解析為合適的類型(int,accumulate),觸發(fā)對應的行為(sum,max)。
最后通過print打印結果。
ArgumentParser.add_argument()
使用方法如下:

ArgumentParser構造對象
ArgumentParser對象的初始化原型為(構造方法):
- 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)
其各個參數(shù)為:
prog - 程序的名稱(默認為sys.argv[0])
usage - 描述程序使用幫助信息(默認值:從添加的參數(shù)來生成)
description - 在參數(shù)幫助之前顯示的文本(默認值:無)
epilog - 在參數(shù)幫助后顯示的文本(默認值:無)
parents - ArgumentParser也應包含其參數(shù)的對象列表
formatter_class - 用于自定義幫助輸出的類
prefix_chars - 前綴可選參數(shù)的字符集(默認值:' - ')
fromfile_prefix_chars -文件前綴字符參數(shù)(默認值:None)
argument_default -為參數(shù)的全局默認值(默認值:None)
conflict_handler - 解決沖突選項的策略(通常不需要)
add_help -添加-h/--help選項解析器(默認值:True)
一般情況下,我們無需管其他情況,只需設置個程序描述即可。
add_argument()方法
add_argument()方法用來創(chuàng)建解析參數(shù),定義對參數(shù)具體的解析,其原型如下:
add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])
參數(shù)有:
name或者flags - 名稱或選項字符串列表,必填寫項。例如foo或-f, --foo。
action - 在命令行中遇到此參數(shù)時要采取的操作類型。
nargs – 要使用的命令行參數(shù)的數(shù)量,用通配符號表示,比如"*","+"和"?",分別表示不限數(shù)量,至少一個和一個參數(shù)。
const - 一些動作和 nargs 選擇所需的常量。
default - 如果參數(shù)在命令行中不存在,則默認使用的值。
type - 命令行參數(shù)應該轉(zhuǎn)換的類型,默認為字符串string
choices – 可選項, 一個允許參數(shù)值的容器。
required - 是否可以省略命令行選項(僅對可選項)。
help - 幫助提示信息,用來對參數(shù)進行必要的描述。
metavar – 幫助消息替代參數(shù)的顯示名稱。
dest - 要添加到parse_args()函數(shù)返回對象的屬性的名稱。

parse_args()方法
parse_args方法用來把命令行中的字符,解析到參數(shù)解析命名空間(add_argumnet()創(chuàng)建)定義的各個參數(shù)。其原型如下:
- parse_args(args=None, namespace=None)
args為參數(shù)列表,參數(shù)字符串通過調(diào)用sys.argv獲取,Namespace為屬性創(chuàng)建一個新的空對象。
實例學習sqlmap
最后我們節(jié)選一個著名sql注入滲透工具sqlmap的cmdline.py部分代碼,來展示一個大型軟件中,如何實際通過argparser來構建起命令行界面的。基本上也跟上面流程方法一樣,不過額外使用一些函數(shù)和功能。

全部代碼詳見sqlmap github官方倉庫:
(github /sqlmapproject/sqlmap/blob/master/lib/parse/cmdline.py)
總結
本文我們實例介紹了python下構建優(yōu)雅終端命令行界面兩種方法Click和argparser。善用他們可以為我們的代碼工作減少很多繁瑣的命令行參數(shù)的定義和解析過程,提高編碼的效率。