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.backendeditlineCmd 將會使用 '^I' 而不是 'tab'。請注意,其他值不會被這樣處理,可能只適用於特定的後端。

可選引數 stdinstdout 指定 Cmd 例項或其子類的例項將用於輸入和輸出的檔案物件。如果未指定,它們將預設為 sys.stdinsys.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 非破壞性地向左移動游標,等等)。

輸入時的檔案結束符(end-of-file)會作為字串 'EOF' 返回。

一個直譯器例項將識別一個命令名 foo 當且僅當它有一個方法 do_foo()。作為一種特殊情況,以字元 '?' 開頭的行將被分派給方法 do_help()。作為另一種特殊情況,以字元 '!' 開頭的行將被分派給方法 do_shell()(如果定義了該方法)。

postcmd() 方法返回一個真值時,此方法將返回。postcmd()stop 引數是命令對應的 do_*() 方法的返回值。

如果啟用了補全,命令的補全將自動完成,而命令引數的補全透過呼叫 complete_foo() 並附帶引數 text, line, begidxendidx 來完成。text 是我們試圖匹配的字串字首:所有返回的匹配項都必須以它開頭。line 是當前輸入行,去除了前導空格。begidxendidx 是字首文字的開始和結束索引,可以根據引數所在的位置提供不同的補全。

Cmd.do_help(arg)

所有 Cmd 的子類都繼承了一個預定義的 do_help()。當使用引數 'bar' 呼叫此方法時,它會呼叫相應的方法 help_bar(),如果該方法不存在,則列印 do_bar() 的文件字串(如果可用)。如果沒有引數,do_help() 會列出所有可用的幫助主題(即所有帶有相應 help_*() 方法的命令或帶有文件字串的命令),並列出任何未文件化的命令。

Cmd.onecmd(str)

將引數解釋為如同在提示符下輸入的一樣。這個方法可以被重寫,但通常不需要;有用的執行掛鉤請參見 precmd()postcmd() 方法。返回值是一個標誌,指示直譯器是否應該停止對命令的解釋。如果對於命令 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 中是一個存根;它存在是為了被子類重寫。返回值將作為 onecmd() 方法要執行的命令;precmd() 的實現可以重寫命令,或者直接返回未改變的 line

Cmd.postcmd(stop, line)

在命令分派完成後立即執行的鉤子方法。這個方法在 Cmd 中是一個存根;它存在是為了被子類重寫。line 是被執行的命令列,stop 是一個標誌,指示在呼叫 postcmd() 之後是否將終止執行;這將是 onecmd() 方法的返回值。此方法的返回值將用作對應於 stop 的內部標誌的新值;返回 false 將導致解釋繼續。

Cmd.preloop()

在呼叫 cmdloop() 時執行一次的鉤子方法。這個方法在 Cmd 中是一個存根;它存在是為了被子類重寫。

Cmd.postloop()

cmdloop() 即將返回時執行一次的鉤子方法。這個方法在 Cmd 中是一個存根;它存在是為了被子類重寫。

Cmd 子類的例項有一些公共例項變數

Cmd.prompt

為請求輸入而發出的提示符。

Cmd.identchars

可被接受為命令字首的字串。

Cmd.lastcmd

所見的最後一個非空命令字首。

Cmd.cmdqueue

一個包含已入隊輸入行的列表。當需要新輸入時,cmdloop() 會檢查 cmdqueue 列表;如果它非空,其元素將按順序被處理,就好像在提示符下輸入的一樣。

Cmd.intro

作為介紹或橫幅發出的字串。可以透過給 cmdloop() 方法一個引數來重寫。

Cmd.doc_header

如果幫助輸出有針對已文件化命令的部分,則發出此標題。

Cmd.misc_header

如果幫助輸出有針對其他幫助主題的部分(即,存在沒有相應 do_*() 方法的 help_*() 方法),則發出此標題。

Cmd.undoc_header

如果幫助輸出有針對未文件化命令的部分(即,存在沒有相應 help_*() 方法的 do_*() 方法),則發出此標題。

Cmd.ruler

用於在幫助資訊標題下繪製分隔線的字元。如果為空,則不繪製標尺線。它預設為 '='

Cmd.use_rawinput

一個標誌,預設為真。如果為真,cmdloop() 使用 input() 來顯示提示並讀取下一個命令;如果為假,則使用 sys.stdout.write()sys.stdin.readline()。(這意味著透過匯入 readline,在支援它的系統上,直譯器將自動支援類似 Emacs 的行編輯和命令歷史按鍵。)

Cmd 示例

cmd 模組主要用於構建自定義的 shell,讓使用者可以與程式進行互動式工作。

本節展示了一個簡單的示例,說明如何圍繞 turtle 模組中的幾個命令構建一個 shell。

forward() 這樣的基本海龜命令被新增到一個 Cmd 子類中,方法名為 do_forward()。引數被轉換為數字並分派給 turtle 模組。文件字串被用於 shell 提供的幫助工具中。

該示例還包括一個基本的錄製和回放功能,透過 precmd() 方法實現,該方法負責將輸入轉換為小寫並將命令寫入檔案。do_playback() 方法讀取檔案並將錄製的命令新增到 cmdqueue 中以便立即回放。

import cmd, sys
from turtle import *

class TurtleShell(cmd.Cmd):
    intro = 'Welcome to the turtle shell.   Type help or ? to list commands.\n'
    prompt = '(turtle) '
    file = None

    # ----- basic turtle commands -----
    def do_forward(self, arg):
        'Move the turtle forward by the specified distance:  FORWARD 10'
        forward(*parse(arg))
    def do_right(self, arg):
        'Turn turtle right by given number of degrees:  RIGHT 20'
        right(*parse(arg))
    def do_left(self, arg):
        'Turn turtle left by given number of degrees:  LEFT 90'
        left(*parse(arg))
    def do_goto(self, arg):
        'Move turtle to an absolute position with changing orientation.  GOTO 100 200'
        goto(*parse(arg))
    def do_home(self, arg):
        'Return turtle to the home position:  HOME'
        home()
    def do_circle(self, arg):
        'Draw circle with given radius an options extent and steps:  CIRCLE 50'
        circle(*parse(arg))
    def do_position(self, arg):
        'Print the current turtle position:  POSITION'
        print('Current position is %d %d\n' % position())
    def do_heading(self, arg):
        'Print the current turtle heading in degrees:  HEADING'
        print('Current heading is %d\n' % (heading(),))
    def do_color(self, arg):
        'Set the color:  COLOR BLUE'
        color(arg.lower())
    def do_undo(self, arg):
        'Undo (repeatedly) the last turtle action(s):  UNDO'
    def do_reset(self, arg):
        'Clear the screen and return turtle to center:  RESET'
        reset()
    def do_bye(self, arg):
        'Stop recording, close the turtle window, and exit:  BYE'
        print('Thank you for using Turtle')
        self.close()
        bye()
        return True

    # ----- record and playback -----
    def do_record(self, arg):
        'Save future commands to filename:  RECORD rose.cmd'
        self.file = open(arg, 'w')
    def do_playback(self, arg):
        'Playback commands from a file:  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(line, file=self.file)
        return line
    def close(self):
        if self.file:
            self.file.close()
            self.file = None

def parse(arg):
    'Convert a series of zero or more numbers to an argument tuple'
    return tuple(map(int, arg.split()))

if __name__ == '__main__':
    TurtleShell().cmdloop()

以下是一個使用 turtle shell 的示例會話,展示了幫助功能、使用空行重複命令以及簡單的錄製和回放功能

Welcome to the turtle shell.   Type help or ? to list commands.

(turtle) ?

Documented commands (type help <topic>):
========================================
bye     color    goto     home  playback  record  right
circle  forward  heading  left  position  reset   undo

(turtle) help forward
Move the turtle forward by the specified distance:  FORWARD 10
(turtle) record spiral.cmd
(turtle) position
Current position is 0 0

(turtle) heading
Current heading is 0

(turtle) reset
(turtle) circle 20
(turtle) right 30
(turtle) circle 40
(turtle) right 30
(turtle) circle 60
(turtle) right 30
(turtle) circle 80
(turtle) right 30
(turtle) circle 100
(turtle) right 30
(turtle) circle 120
(turtle) right 30
(turtle) circle 120
(turtle) heading
Current heading is 180

(turtle) forward 100
(turtle)
(turtle) right 90
(turtle) forward 100
(turtle)
(turtle) right 90
(turtle) forward 400
(turtle) right 90
(turtle) forward 500
(turtle) right 90
(turtle) forward 400
(turtle) right 90
(turtle) forward 300
(turtle) playback spiral.cmd
Current position is 0 0

Current heading is 0

Current heading is 180

(turtle) bye
Thank you for using Turtle