cmd — 面向行的命令解释器支持

源代码: Lib/cmd.py


Cmd 类为编写面向行的命令解释器提供了一个简单的框架。这些通常对于测试工具、管理工具以及稍后将包装在更复杂接口中的原型很有用。

class cmd.Cmd(completekey='tab', stdin=None, stdout=None)

Cmd 实例或子类实例是一个面向行的解释器框架。没有充分的理由实例化 Cmd 本身;相反,它作为您自己定义的解释器类的超类很有用,以便继承 Cmd 的方法并封装操作方法。

可选参数 completekeyreadline 的补全键的名称;默认为 Tab。如果 completekey 不是 Nonereadline 可用,则会自动进行命令补全。

默认值 'tab' 会被特殊处理,以便在每个 readline.backend 上都指 Tab 键。具体来说,如果 readline.backendeditline,则 Cmd 将使用 '^I' 而不是 'tab'。请注意,其他值不会以这种方式处理,可能只适用于特定的后端。

可选参数 stdinstdout 指定 Cmd 实例或子类实例将用于输入和输出的输入和输出文件对象。如果未指定,则它们将默认为 sys.stdinsys.stdout

如果要使用给定的 stdin,请确保将实例的 use_rawinput 属性设置为 False,否则将忽略 stdin

在 3.13 版本中变更: completekey='tab' 对于 editline 被替换为 '^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_*() 方法的返回值。

如果启用了补全,则将自动完成命令,并且通过使用参数 textlinebegidxendidx 调用 complete_foo() 来完成命令参数。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

一个标志,默认为 true。如果为 true,则 cmdloop() 使用 input() 来显示提示符并读取下一个命令;如果为 false,则使用 sys.stdout.write()sys.stdin.readline()。(这意味着通过导入 readline,在支持它的系统上,解释器将自动支持类似 Emacs 的行编辑和命令历史按键。)

Cmd 示例

cmd 模块主要用于构建自定义 shell,使用户可以交互地使用程序。

本节提供了一个简单示例,说明如何围绕 turtle 模块中的几个命令构建 shell。

基本的 turtle 命令,例如 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