cmd --- 以列為導向的命令直譯器支援¶
原始碼:Lib/cmd.py
Cmd 類別提供了一個簡單的架構,用於撰寫列導向的命令直譯器。這類直譯器常用於測試控制工具、管理工具以及日後將包裝於更高階介面的原型中。
- class cmd.Cmd(completekey='tab', stdin=None, stdout=None)¶
Cmd實例或其子類別實例是一種列導向的直譯器架構。通常沒有必要直接實例化Cmd本身;它更適合作為你自己定義的直譯器類別的父類別,讓你能繼承Cmd的方法,並封裝動作方法。可選引數 completekey 是
readline模組中用於自動完成的按鍵名稱;預設為 Tab。若 completekey 不為None且readline可用,則會自動啟用命令自動完成功能。預設值
'tab'會被特殊處理,使其在所有的readline.backend中皆代表 Tab 鍵。具體來說,若readline.backend為editline,則Cmd會改用'^I'取代'tab'。請注意,其他值不會有此處理方式,且可能僅能在特定的後端中適用。可選引數 stdin 與 stdout 用來指定 Cmd 實例或其子類別實例所使用的輸入與輸出檔案物件。若未指定,預設為
sys.stdin與sys.stdout。若你希望使用指定的 stdin,請務必將該實例的
use_rawinput屬性設為False,否則 stdin 會被忽略。在 3.13 版的變更: 對於
editline,completekey='tab'會被替換為'^I'。
Cmd 物件¶
Cmd 實例具有以下方法:
- Cmd.cmdloop(intro=None)¶
重複顯示提示字元、接收輸入、剖析接收到的輸入字串前綴,並派發給動作方法,將其餘部分作為引數傳遞給它們
此可選引數為橫幅或導言字串,會在首次顯示提示字元前輸出(此值會覆寫
intro類別屬性)。如果已載入
readline模組,輸入將自動繼承類似 bash 的歷史紀錄編輯功能(例如 Control-P 可向上捲動至上一個命令,Control-N 向下捲動至下一個命令,Control-F 非破壞性地將游標向右移動,Control-B 非破壞性地將游標向左移動等)。當輸入為檔案結尾(EOF)時,會傳回字串
'EOF'。直譯器實例僅當存在
do_foo()方法時,才會識別命令名稱foo。作為特殊情況,以字元'?'開頭的列會被派發至do_help()方法;另一個特殊情況是,以字元'!'開頭的列會被派發至do_shell()方法(若該方法已定義)。當
postcmd()方法回傳真值時,此方法將會結束。傳遞給postcmd()的 stop 引數是該命令對應的do_*()方法的回傳值。如果啟用了自動完成,命令的自動完成將會自動執行,而命令引數的自動完成則是透過呼叫
complete_foo()方法並傳入 text、line、begidx 和 endidx 引數來處理。text 是要比對的字串前綴:所有回傳的符合項都必須以此字串開頭。line 是目前的輸入列(前置空白會被移除),begidx 和 endidx 則分別是前綴字串的起始與結束索引,可用來根據引數所在的位置提供不同的自動完成結果。
- Cmd.do_help(arg)¶
所有
Cmd的子類別都會繼承預先定義的do_help()方法。當此方法接收到引數'bar'時,會呼叫對應的help_bar()方法;若該方法不存在,則會列印do_bar()的說明字串(若有的話)。若未提供任何引數,do_help()會列出所有可用的說明主題(也就是所有具有對應help_*()方法或有說明字串的命令),並且也會列出所有尚未記錄的命令。
- Cmd.onecmd(str)¶
將引數視為在回應提示字元時所輸入的內容來直譯。這個方法可以被覆寫,但通常不需要這麼做;參見
precmd()與postcmd()方法,它們提供實用的執行勾點(hook)。此方法的回傳值是一個旗標,用來指出是否應該停止直譯器對命令的直譯。若有對應 str 命令的do_*()方法,則會回傳該方法的回傳值;否則,回傳值將來自default()方法。
- Cmd.emptyline()¶
在回應提示字元時輸入空白列,會呼叫此方法。若此方法未被覆寫,則會重復上一次輸入的非空命令。
- Cmd.default(line)¶
當輸入列中的命令前綴無法辨識時,會呼叫此方法。若此方法未被覆寫,則會輸出並回傳錯誤訊息。
- Cmd.completedefault(text, line, begidx, endidx)¶
當沒有對應特定命令的
complete_*()方法時,會呼叫此方法以完成輸入列。預設會回傳空串列。
- Cmd.columnize(list, displaywidth=80)¶
此方法用來將字串串列顯示為緊湊的欄集合。每一欄的寬度僅足以容納其內容,各欄之間以兩個空格分隔,以提高可讀性。
- Cmd.precmd(line)¶
勾點方法會在直譯命令列 line 前執行,但會在提示字元產生並顯示後才觸發。這個方法在
Cmd類別中為 stub,預期由子類別覆寫。回傳值會作為onecmd()方法所執行的命令;precmd()的實作可以重寫該命令,或直接回傳未變更的 line。
- Cmd.postcmd(stop, line)¶
勾點方法會在命令派發完成後執行。這個方法在
Cmd類別中為 stub,預期由子類別覆寫。line 是剛剛執行的命令列,而 stop 是一個旗標,用來指出在呼叫postcmd()後是否應終止執行;該值即為onecmd()方法的回傳值。本方法的回傳值將會更新內部的 stop 旗標;若回傳 false,則會繼續進行直譯。
Cmd 子類別的實例包含一些公開的實例變數:
- Cmd.prompt¶
用來請求輸入的提示字元。
- Cmd.identchars¶
可作為命令前綴的字元字串
- Cmd.lastcmd¶
最後一個遇到的非空命令前綴
- Cmd.doc_header¶
若說明輸出包含已記錄命令的區段,則會顯示的標頭字串。
- Cmd.misc_header¶
若說明輸出包含雜項說明主題的區段(也就是存在
help_*()方法但沒有對應的do_*()方法),則會顯示的標頭字串。
- Cmd.undoc_header¶
若說明輸出包含未記錄命令的區段(也就是存在
do_*()方法但沒有對應的help_*()方法),則會顯示的標頭字串。
- Cmd.ruler¶
用於在說明訊息的標頭下方繪製分隔線的字元。若為空,則不會繪製分隔線。預設為
'='。
- Cmd.use_rawinput¶
一個旗標,預設為 true。若為 true,
cmdloop()會使用input()來顯示提示字元並讀取下一個命令;若為 false,則會改用sys.stdout.write()和sys.stdin.readline()。(這表示在支援的系統中,透過 importreadlinemodule,直譯器將自動支援類似 Emacs 的列編輯與命令歷史快捷鍵。)
Cmd 範例¶
cmd module 主要用於建構自訂 shell,讓使用者能以互動方式操作程式。
本節將示範如何以 turtle module 中的幾個命令為基礎,建立一個簡單的 shell。
像是 forward() 這樣的基本 turtle 命令,可透過新增名為 do_forward() 的方法加入至 Cmd 子類別中。傳入的引數會轉換為數值,並傳送給 turtle 模組。該方法的說明字串會用於 shell 所提供的說明功能中。
此範例同時包含一個簡單的錄製與重播功能,其實作方式是透過 precmd() 方法,負責將輸入轉為小寫並寫入檔案。do_playback() 方法則會讀取該檔案,並將錄製的命令加入 cmdqueue 中以供立即重播:
import cmd, sys
from turtle import *
class TurtleShell(cmd.Cmd):
intro = '歡迎來到 turtle shell。輸入 help 或 ? 來列出命令。\n'
prompt = '(turtle) '
file = None
# ----- 基本烏龜命令 -----
def do_forward(self, arg):
'將烏龜向前移動指定的距離: FORWARD 10'
forward(*parse(arg))
def do_right(self, arg):
'將烏龜右轉指定的角度: RIGHT 20'
right(*parse(arg))
def do_left(self, arg):
'將烏龜左轉指定的角度: LEFT 90'
left(*parse(arg))
def do_goto(self, arg):
'將烏龜移動到指定的絕對位置並改變方向。 GOTO 100 200'
goto(*parse(arg))
def do_home(self, arg):
'將烏龜返回起始位置: HOME'
home()
def do_circle(self, arg):
'畫出指定半徑、範圍和步數的圓: CIRCLE 50'
circle(*parse(arg))
def do_position(self, arg):
'顯示目前烏龜位置: POSITION'
print('目前位置是 %d %d\n' % position())
def do_heading(self, arg):
'顯示目前烏龜方向角度: HEADING'
print('目前方向是 %d\n' % (heading(),))
def do_color(self, arg):
'設定顏色: COLOR BLUE'
color(arg.lower())
def do_undo(self, arg):
'撤銷(重複)最後一次烏龜動作: UNDO'
def do_reset(self, arg):
'清除畫面並將烏龜返回到中心: RESET'
reset()
def do_bye(self, arg):
'停止錄製、關閉烏龜視窗並退出: BYE'
print('感謝使用 Turtle')
self.close()
bye()
return True
# ----- 錄製與重播 -----
def do_record(self, arg):
'將未來命令儲存至檔案: RECORD rose.cmd'
self.file = open(arg, 'w')
def do_playback(self, arg):
'從檔案重播命令: PLAYBACK rose.cmd'
self.close()
with open(arg) as f:
self.cmdqueue.extend(f.read().splitlines())
def precmd(self, line):
line = line.lower()
if self.file and 'playback' not in line:
print