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
输出显示 person
是greet
的一个可选参数。尽管对命令来说键入参数是可选的,但该命令的处理方法是始终需要参数的。当不主动键入参数时,方法的参数是一个空字符串。然后由命令处理程序来确定空参数是否有效,或者对命令进行任何进一步的解析和处理。在此示例中,如果提供了一个人的名字,则问候语是个性化的。
(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_header
,misc_header
,undoc_header
和ruler
属性用于格式化输出。
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]
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 cc 协议,如果我们的工作有侵犯到您的权益,请及时联系江南app体育官方入口。