帶你了解3個Python命令行工具
用 Click、Docopt 和 Fire 庫寫你自己的命令行應用。
有時對于某項工作來說一個命令行工具就足以勝任。命令行工具是一種從你的 shell 或者終端之類的地方交互或運行的程序。Git 和 Curl 就是兩個你也許已經很熟悉的命令行工具。
當你有一小段代碼需要在一行中執行多次或者經常性地被執行,命令行工具就會很有用。Django 開發者執行 ./manage.py runserver 命令來啟動他們的網絡服務器;Docker 開發者執行 docker-compose up 來啟動他們的容器。你想要寫一個命令行工具的原因可能和你一開始想寫代碼的原因有很大不同。
對于這個月的 Python 專欄,我們有 3 個庫想介紹給希望為自己編寫命令行工具的 Python 使用者。
Click
Click 是我們***的用來開發命令行工具的 Python 包。其:
- 有一個富含例子的出色文檔
- 包含說明如何將命令行工具打包成一個更加易于執行的 Python 應用程序
- 自動生成實用的幫助文本
- 使你能夠疊加使用可選和必要參數,甚至是 多個命令
- 有一個 Django 版本(
django-click)用來編寫管理命令
Click 使用 @click.command() 去聲明一個函數作為命令,同時可以指定必要和可選參數。
# hello.pyimport click@click.command()@click.option('--name', default='', help='Your name')def say_hello(name):click.echo("Hello {}!".format(name))if __name__ == '__main__':say_hello()
@click.option() 修飾器聲明了一個 可選參數 ,而 @click.argument() 修飾器聲明了一個 必要參數。你可以通過疊加修飾器來組合可選和必要參數。echo() 方法將結果打印到控制臺。
$ python hello.py --name='Lacey'Hello Lacey!
Docopt
Docopt 是一個命令行工具的解析器,類似于命令行工具的 Markdown。如果你喜歡流暢地編寫應用文檔,在本文推薦的庫中 Docopt 有著***的格式化幫助文本。它不是我們***的命令行工具開發包的原因是它的文檔猶如把人扔進深淵,使你開始使用時會有一些小困難。然而,它仍是一個輕量級的、廣受歡迎的庫,特別是當一個漂亮的說明文檔對你來說很重要的時候。
Docopt 對于如何格式化文章開頭的 docstring 是很特別的。在工具名稱后面的 docsring 中,頂部元素必須是 Usage: 并且需要列出你希望命令被調用的方式(比如:自身調用,使用參數等等)。Usage: 需要包含 help 和 version 參數。
docstring 中的第二個元素是 Options:,對于在 Usages: 中提及的可選項和參數,它應當提供更多的信息。你的 docstring 的內容變成了你幫助文本的內容。
"""HELLO CLIUsage:hello.pyhello.py <name>hello.py -h|--helphello.py -v|--versionOptions:<name> Optional name argument.-h --help Show this screen.-v --version Show version."""from docopt import docoptdef say_hello(name):return("Hello {}!".format(name))if __name__ == '__main__':arguments = docopt(__doc__, version='DEMO 1.0')if arguments['<name>']:print(say_hello(arguments['<name>']))else:print(arguments)
在最基本的層面,Docopt 被設計用來返回你的參數鍵值對。如果我不指定上述的 name 調用上面的命令,我會得到一個字典的返回值:
$ python hello.py{'--help': False,'--version': False,'<name>': None}
這里可看到我沒有輸入 help 和 version 標記并且 name 參數是 None。
但是如果我帶著一個 name 參數調用,say_hello 函數就會執行了。
$ python hello.py JeffHello Jeff!
Docopt 允許同時指定必要和可選參數,且各自有著不同的語法約定。必要參數需要在 ALLCAPS 和 <carets> 中展示,而可選參數需要單雙橫杠顯示,就像 --like。更多內容可以閱讀 Docopt 有關 patterns 的文檔。
Fire
Fire 是谷歌的一個命令行工具開發庫。尤其令人喜歡的是當你的命令需要更多復雜參數或者處理 Python 對象時,它會聰明地嘗試解析你的參數類型。
Fire 的 文檔 包括了海量的樣例,但是我希望這些文檔能被更好地組織。Fire 能夠處理 同一個文件中的多條命令、使用 對象 的方法作為命令和 分組 命令。
它的弱點在于輸出到控制臺的文檔。命令行中的 docstring 不會出現在幫助文本中,并且幫助文本也不一定標識出參數。
import firedef say_hello(name=''):return 'Hello {}!'.format(name)if __name__ == '__main__':fire.Fire()
參數是必要還是可選取決于你是否在函數或者方法定義中為其指定了一個默認值。要調用命令,你必須指定文件名和函數名,比較類似 Click 的語法:
$ python hello.py say_hello RikkiHello Rikki!
你還可以像標記一樣傳參,比如 --name=Rikki。
額外贈送:打包!
Click 包含了使用 setuptools 打包 命令行工具的使用說明(強烈推薦按照說明操作)。
要打包我們***個例子中的命令行工具,將以下內容加到你的 setup.py 文件里:
from setuptools import setupsetup(name='hello',version='0.1',py_modules=['hello'],install_requires=['Click',],entry_points='''[console_scripts]hello=hello:say_hello''',)
任何你看見 hello 的地方,使用你自己的模塊名稱替換掉,但是要記得忽略 .py 后綴名。將 say_hello 替換成你的函數名稱。
然后,執行 pip install --editable 來使你的命令在命令行中可用。
現在你可以調用你的命令,就像這樣:
$ hello --name='Jeff'Hello Jeff!
通過打包你的命令,你可以省掉在控制臺鍵入 python hello.py --name='Jeff' 這種額外的步驟以減少鍵盤敲擊。這些指令也很可能可在我們提到的其他庫中使用。






















