15.6. cmd — 命令行处理器 | 应用程序组成元素 |《python 3 标准库实例教程》| python 技术论坛-江南app体育官方入口

未匹配的标注

目的:创建面向行的命令处理器/create line-oriented command processors。

cmd模块包含一个公共类cmd,旨在用作交互式 shell 和其他命令解释器的基类。默认情况下,它使用 进行交互式提示处理,命令行编辑,并完成命令。

处理命令

cmd创建的命令解释器使用循环从其输入中读取所有行,解析它们,然后将命令分派给适当的命令处理程序。输入行被分为两部分:命令和该行上的任何其他文本。如果用户输入foo barbar,并且解释器类包含名为do_foo()的方法,则以“ barbar”作为唯一参数来调用它。

文件结束标记被分派到do_eof()。如果命令处理程序返回一个真值,则程序将干净退出。因此,为了提供一种退出解释器的干净方法,请确保实现do_eof() 并使它返回 true。

这个简单的示例程序支持“ greet ”命令:

cmd_simple.py

import cmd
class helloworld(cmd.cmd):
    def do_greet(self, line):
        print("hello")
    def do_eof(self, line):
        return true
if __name__ == '__main__':
    helloworld().cmdloop()

交互运行将演示如何调度命令,并显示cmd中包含的某些功能。

$ python3 cmd_simple.py
(cmd)

首先要注意的是命令提示符(cmd) 。可以通过属性prompt配置提示。提示值是动态的,并且如果命令处理程序更改了提示属性,则新值将用于查询下一个命令。

documented commands (type help <topic>):
========================================
help
undocumented commands:
======================
eof  greet

help命令内置于cmd中。不带任何参数的help将显示可用命令列表。如果输入中包含命令名称,则输出更详细,并且仅在可用时限制于该命令的详细信息。

如果命令是greet,则调用do_greet() 来处理它:

(cmd) greet
hello

如果输入的命令没有对应的处理函数,则方法default()函数被自动调用且整个输入行作为它的参数。该方法默认报告一个错误。

(cmd) foo
*** unknown syntax: foo

由于do_eof()返回 true,因此输入 ctrl-d 将导致解释器退出。

(cmd) ^d$

命令参数

此示例包括一些增强功能,以消除一些烦恼并为greet命令添加帮助。

cmd_arguments.py

import cmd
class helloworld(cmd.cmd):
    def do_greet(self, person):
        """greet [person]
        greet the named person"""
        if person:
            print("hi,", person)
        else:
            print('hi')
    def do_eof(self, line):
        return true
    def postloop(self):
        print()
if __name__ == '__main__':
    helloworld().cmdloop()

添加到do_greet() 的文档字符串成为该命令的帮助文本:

$ python3 cmd_arguments.py
(cmd) help
documented commands (type help <topic>):
========================================
greet  help
undocumented commands:
======================
eof
(cmd) help greet
greet [person]
        greet the named person

输出显示 persongreet 的一个可选参数。尽管对命令来说键入参数是可选的,但该命令的处理方法是始终需要参数的。当不主动键入参数时,方法的参数是一个空字符串。然后由命令处理程序来确定空参数是否有效,或者对命令进行任何进一步的解析和处理。在此示例中,如果提供了一个人的名字,则问候语是个性化的。

(cmd) greet alice
hi, alice
(cmd) greet
hi

无论用户是否提供参数,传递给命令处理程序的值都不包括命令本身。这简化了命令处理程序中的解析,尤其是在需要多个参数的情况下。

实时帮助

在前面的示例中,帮助文本的格式尚待改进。由于它来自文档字符串,因此保留了源文件中的缩进。可以直接更改源代码以删除多余的空格,但是这会使该程序代码的格式不正确。更好的江南app体育官方入口的解决方案是在名为help_greet()的方法中为 greet 命令实现帮助处理程序。调用帮助处理程序以生成命名命令的帮助文本。

cmd_do_help.py

# set up gnureadline as readline if installed.
try:
    import gnureadline
    import sys
    sys.modules['readline'] = gnureadline
except importerror:
    pass
import cmd
class helloworld(cmd.cmd):
    def do_greet(self, person):
        if person:
            print("hi,", person)
        else:
            print('hi')
    def help_greet(self):
        print('.'.join([
            'greet [person]',
            'greet the named person',
        ]))
    def do_eof(self, line):
        return true
if __name__ == '__main__':
    helloworld().cmdloop()

在此示例中,文本是静态的,但格式更美观。也可以使用先前的命令状态将帮助文本的内容调整为当前上下文。

$ python3 cmd_do_help.py
(cmd) help greet
greet [person]
greet the named person

帮助处理程序要实际输出帮助消息,而不是简单地返回帮助文本以在其他地方处理。

自动完成

cmd支持对带有处理方法的命令的名称自动补全。用户通过在输入提示下按 tab 键来触发完成。如果存在多个可能补全的命令,请按两次 tab 键以打印选项列表。

注意

默认情况下,readline所需的gnu库并非在所有平台上都可用。在这种情况下,制表符补全可能不起作用。请参阅[readline]( readline:gnu readline库”),以获取有关在安装python时安装必要库的提示没有它们。

$ python3 cmd_do_help.py
(cmd) <tab><tab>
eof    greet  help
(cmd) h<tab>
(cmd) help

知道命令后,该命令处理方法的参数由前缀为complete_的方法处理完成。这允许完成处理程序(complete_)使用任意条件来组合可能完成的列表 (例如查询数据库或查看文件系统上的文件或目录) 。在下面的例程中,该程序具有一组硬编码的 “朋友”,与命名或匿名陌生人相比,他们收到的正式问候较少。真正的程序可能会将列表保存在某处,并读取一次,然后根据需要缓存
要扫描的内容。

cmd_arg_completion.py

# set up gnureadline as readline if installed.
try:
    import gnureadline
    import sys
    sys.modules['readline'] = gnureadline
except importerror:
    pass
import cmd
class helloworld(cmd.cmd):
    friends = ['alice', 'adam', 'barbara', 'bob']
    def do_greet(self, person):
        "greet the person"
        if person and person in self.friends:
            greeting = 'hi, {}!'.format(person)
        elif person:
            greeting = 'hello, {}'.format(person)
        else:
            greeting = 'hello'
        print(greeting)
    def complete_greet(self, text, line, begidx, endidx):
        if not text:
            completions = self.friends[:]
        else:
            completions = [
                f
                for f in self.friends
                if f.startswith(text)
            ]
        return completions
    def do_eof(self, line):
        return true
if __name__ == '__main__':
    helloworld().cmdloop()

输入文本时,complete_greet()返回匹配的朋友列表。否则,将返回完整的朋友列表。

$ python3 cmd_arg_completion.py
(cmd) greet <tab><tab>
adam     alice    barbara  bob
(cmd) greet a<tab><tab>
adam   alice
(cmd) greet ad<tab>
(cmd) greet adam
hi, adam!

如果给定的名字不在朋友列表中,则给出正式问候。

(cmd) greet joe
hello, joe

覆盖基类方法

cmd包括几种方法,这些方法可以作为执行操作或更改基类行为的钩子而重写。该示例不够全面的,但包含许多有用的方法。

cmd_illustrate_methods.py

# set up gnureadline as readline if installed.
try:
    import gnureadline
    import sys
    sys.modules['readline'] = gnureadline
except importerror:
    pass
import cmd
class illustrate(cmd.cmd):
    "illustrate the base class method use."
    def cmdloop(self, intro=none):
        print('cmdloop({})'.format(intro))
        return cmd.cmd.cmdloop(self, intro)
    def preloop(self):
        print('preloop()')
    def postloop(self):
        print('postloop()')
    def parseline(self, line):
        print('parseline({!r}) =>'.format(line), end='')
        ret = cmd.cmd.parseline(self, line)
        print(ret)
        return ret
    def onecmd(self, s):
        print('onecmd({})'.format(s))
        return cmd.cmd.onecmd(self, s)
    def emptyline(self):
        print('emptyline()')
        return cmd.cmd.emptyline(self)
    def default(self, line):
        print('default({})'.format(line))
        return cmd.cmd.default(self, line)
    def precmd(self, line):
        print('precmd({})'.format(line))
        return cmd.cmd.precmd(self, line)
    def postcmd(self, stop, line):
        print('postcmd({}, {})'.format(stop, line))
        return cmd.cmd.postcmd(self, stop, line)
    def do_greet(self, line):
        print('hello,', line)
    def do_eof(self, line):
        "exit"
        return true
if __name__ == '__main__':
    illustrate().cmdloop('illustrating the methods of cmd.cmd')

cmdloop() 是解释器的主要处理循环。通常不需要重写它,因为 preloop()postloop()钩子都可用改变它的行为。

通过 cmdloop() 进行的每次迭代都会调用 onecmd() 将命令分派到对应的处理程序。用 parseline() 解析实际的输入行以创建一个包含命令和该行其余部分的元组。

如果该行为空,则调用emptyline()。默认实现再次运行前面的命令。如果该行包含命令,则首先调用precmd(),然后查找并调用处理程序。如果未找到,则调用default()。最后调用postcmd()

这是添加了print语句的示例会话:

$ python3 cmd_illustrate_methods.py
cmdloop(illustrating the methods of cmd.cmd)
preloop()
illustrating the methods of cmd.cmd
(cmd) greet bob
precmd(greet bob)
onecmd(greet bob)
parseline(greet bob) => ('greet', 'bob', 'greet bob')
hello, bob
postcmd(none, greet bob)
(cmd) ^dprecmd(eof)
onecmd(eof)
parseline(eof) => ('eof', '', 'eof')
postcmd(true, eof)
postloop()

通过属性配置cmd

除了前面介绍的方法外,还有一些用于控制命令解释器的属性。可以将提示设置为每次要求用户输入新命令时要打印的字符串。 简介是程序开始时显示的“欢迎”消息。 cmdloop() 接受该值的参数,或者可以直接在类上设置它。打印帮助时,doc_headermisc_headerundoc_headerruler属性用于格式化输出。
cmd_attributes.py

import cmd
class helloworld(cmd.cmd):
    prompt = 'prompt: '
    intro = "simple command processor example."
    doc_header = 'doc_header'
    misc_header = 'misc_header'
    undoc_header = 'undoc_header'
    ruler = '-'
    def do_prompt(self, line):
        "change the interactive prompt"
        self.prompt = line  ': '
    def do_eof(self, line):
        return true
if __name__ == '__main__':
    helloworld().cmdloop()

该示例类显示了一个命令处理程序,该命令处理程序使用户可以控制交互式会话的提示。

$ python3 cmd_attributes.py
simple command processor example.
prompt: prompt hello
hello: help
doc_header
----------
help  prompt
undoc_header
------------
eof
hello:

##运行shell命令

为了补充标准命令处理,cmd包括两个特殊的命令前缀。问号()等同于内置的help命令,并且可以以相同的方式使用。感叹号()映射到do_shell(),用于“脱壳”以运行其他命令,如本例所示。

cmd_do_shell.py

import cmd
import subprocess
class shellenabled(cmd.cmd):
    last_output = ''
    def do_shell(self, line):
        "run a shell command"
        print("running shell command:", line)
        sub_cmd = subprocess.popen(line,
                                   shell=true,
                                   stdout=subprocess.pipe)
        output = sub_cmd.communicate()[0].decode('utf-8')
        print(output)
        self.last_output = output
    def do_echo(self, line):
        """print the input, replacing '$out' with
        the output of the last shell command.
        """
        # 显然不可靠
        print(line.replace('$out', self.last_output))
    def do_eof(self, line):
        return true
if __name__ == '__main__':
    shellenabled().cmdloop()

echo命令实现将其参数中的字符串$ out替换为上一个shell命令的输出。

$ python3 cmd_do_shell.py
(cmd) ?
documented commands (type help <topic>):
========================================
echo  help  shell
undocumented commands:
======================
eof
(cmd) ? shell
run a shell command
(cmd) ? echo
print the input, replacing '$out' with
        the output of the last shell command
(cmd) shell pwd
running shell command: pwd
.../pymotw-3/source/cmd
(cmd) ! pwd
running shell command: pwd
.../pymotw-3/source/cmd
(cmd) echo $out
.../pymotw-3/source/cmd

##替代输入

虽然cmd()的默认模式是通过[readline]( readline“ readline:gnu readline库”)库,也可以使用标准unix shell重定向将一系列命令传递到标准输入中。

$ echo help | python3 cmd_do_help.py
(cmd)
documented commands (type help <topic>):
========================================
greet  help
undocumented commands:
======================
eof
(cmd)

若要使程序直接读取脚本文件,可能需要进行其他一些更改。由于[readline]( readline:gnu readline库”)与终端/ tty设备进行交互,而不是与标准输入流,要从文件中读取脚本时应将其禁用。另外,为避免打印多余的提示,可以将提示设置为空字符串。此示例显示如何打开文件并将其作为输入传递给helloworld示例的修改版本。

cmd_file.py

import cmd
class helloworld(cmd.cmd):
    #禁用rawinput模块使用
    use_rawinput = false
    #读取每个命令后不显示提示
    prompt = ''
    def do_greet(self, line):
        print("hello,", line)
    def do_eof(self, line):
        return true
if __name__ == '__main__':
    import sys
    with open(sys.argv[1], 'rt') as input:
        helloworld(stdin=input).cmdloop()

use_rawinput设置为false并且将prompt设置为空字符串,可以在输入文件上每行使用一个命令来调用脚本。

cmd_file.txt

greet
greet alice and bob

使用示例输入运行示例脚本将产生以下输出。

$ python3 cmd_file.py cmd_file.txt
hello,
hello, alice and bob

##来自sys.argv的命令

程序的命令行参数也可以作为解释器类的命令处理,而不是从控制台或文件中读取命令。要使用命令行参数,如本例所示,直接调用onecmd()

cmd_argv.py

import cmd
class interactiveorcommandline(cmd.cmd):
    """accepts commands via the normal interactive
    prompt or on the command line.
    """
    def do_greet(self, line):
        print('hello,', line)
    def do_eof(self, line):
        return true
if __name__ == '__main__':
    import sys
    if len(sys.argv) > 1:
        interactiveorcommandline().onecmd(' '.join(sys.argv[1:]))
    else:
        interactiveorcommandline().cmdloop()

由于onecmd()采用单个字符串作为输入,因此在传入之前必须将程序的参数连接在一起。

$ python3 cmd_argv.py greet command-line user
hello, command-line user
$ python3 cmd_argv.py
(cmd) greet interactive user
hello, interactive user
(cmd)

另请参见

-
--具有其他功能的cmd的直接替代。
-[gnu readline]

本文章首发在 江南app体育官方入口 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 cc 协议,如果我们的工作有侵犯到您的权益,请及时联系江南app体育官方入口。

原文地址:https://learnku.com/docs/pymotw/cmd-line...

译文地址:https://learnku.com/docs/pymotw/cmd-line...

上一篇 下一篇
贡献者:3
讨论数量: 0



暂无话题~
网站地图