使用python的cmd.Cmd创建自定义CLI时,如何告诉处理程序中止当前行并给我一个新提示?
这是一个最小的例子:
# console_min.py
# run: 'python console_min.py'
import cmd, signal
class Console(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
self.prompt = "[test] "
signal.signal(signal.SIGINT, handler)
def do_exit(self, args):
return -1
def do_EOF(self, args):
return self.do_exit(args)
def preloop(self):
cmd.Cmd.preloop(self)
self._hist = []
self._locals = {}
self._globals = {}
def postloop(self):
cmd.Cmd.postloop(self)
print "Exiting ..."
def precmd(self, line):
self._hist += [ line.strip() ]
return line
def postcmd(self, stop, line):
return stop
def emptyline(self):
return cmd.Cmd.emptyline(self)
def handler(self, signum, frame):
# self.emptyline() does not work here
# return cmd.Cmd.emptyline(self) does not work here
print "caught ctrl+c, press return to continue"
if __name__ == '__main__':
console = Console()
console.cmdloop()
非常感谢您的进一步帮助。
原始问题及更多详情: [目前以下建议已纳入此问题 - 仍在寻找答案。已更新以修复错误。]
我已经尝试将处理程序移动到循环外的函数中,看它是否更灵活,但它似乎不是。
我正在使用python的cmd.Cmd module创建自己的命令行解释器来管理与某些软件的交互。我经常按ctrl + c,期望返回新提示的流行的类似shell的行为,而不会对输入的任何命令起作用。然而,它只是退出。我试图在代码中的各个点捕获KeyboardInterrupt异常(preloop等)无济于事。我在sigints上读过一些内容,但不太清楚这是怎么回事。
更新:根据以下建议,我尝试实现信号,并且能够这样做,使ctrl + c现在不会退出CLI,但会打印消息。但是,我的新问题是我似乎无法告诉处理程序函数(见下文)在打印之外做很多事情。我想ctrl + c基本上中止当前行并给我一个新的提示。
答案 0 :(得分:13)
我目前正在使用Cmd模块创建Shell。我遇到了同样的问题,我找到了解决方案。
以下是代码:
class Shell(Cmd, object)
...
def cmdloop(self, intro=None):
print(self.intro)
while True:
try:
super(Shell, self).cmdloop(intro="")
break
except KeyboardInterrupt:
print("^C")
...
现在你在shell中有一个合适的KeyboardInterrupt(又名 CTRL-C )处理程序。
答案 1 :(得分:6)
您可以抓住KeyboardInterrupt
加注cmd.Cmd.cmdloop()
,而不是使用信号处理。您当然可以使用信号处理,但这不是必需的。
在while循环中运行cmdloop()
的调用,该循环在KeyboardInterrupt
异常时重新启动,但由于EOF而正常终止。
import cmd
import sys
class Console(cmd.Cmd):
def do_EOF(self,line):
return True
def do_foo(self,line):
print "In foo"
def do_bar(self,line):
print "In bar"
def cmdloop_with_keyboard_interrupt(self):
doQuit = False
while doQuit != True:
try:
self.cmdloop()
doQuit = True
except KeyboardInterrupt:
sys.stdout.write('\n')
console = Console()
console.cmdloop_with_keyboard_interrupt()
print 'Done!'
执行CTRL-c只会在新行上打印新提示。
(Cmd) help
Undocumented commands:
======================
EOF bar foo help
(Cmd) <----- ctrl-c pressed
(Cmd) <------ctrl-c pressed
(Cmd) ddasfjdfaslkdsafjkasdfjklsadfljk <---- ctrl-c pressed
(Cmd)
(Cmd) bar
In bar
(Cmd) ^DDone!
答案 2 :(得分:0)
回应this answer中的以下评论:
如果您的课程除了这似乎是在解决方案上趋同,但我不知道如何将它集成到我自己的代码中(上图)。我必须弄清楚'超级'线。我需要尝试在将来的某个时候使用它。
super()
之外还有object
,那么 cmd.Cmd
将适用于此答案。像这样:
class Console(cmd.Cmd, object):
答案 3 :(得分:0)
我想做同样的事情,因此我进行了搜索并创建了基本的脚本以理解,我的代码可以在我的GitHub
上找到所以,在您的代码中,
代替此
if __name__ == '__main__':
console = Console()
console.cmdloop()
使用此,
if __name__ == '__main__':
console = Console()
try:
console.cmdloop()
except KeyboardInterrupt:
print ("print sth. ")
raise SystemExit
希望这会对您有所帮助。好,对我有用。 ?
答案 4 :(得分:-1)
您可以使用CTRL-C signal抓住signal handler。如果你添加下面的代码,解释器拒绝退出CTRL-C:
import signal
def handler(signum, frame):
print 'Caught CTRL-C, press enter to continue'
signal.signal(signal.SIGINT, handler)
如果您不希望在每个CTRL-C之后按ENTER键,只需让处理程序不执行任何操作,这将捕获信号而不会产生任何影响:
def handler(signum, frame):
""" just do nothing """
答案 5 :(得分:-1)
您可以查看signal
模块:http://docs.python.org/library/signal.html
import signal
oldSignal = None
def handler(signum, frame):
global oldSignal
if signum == 2:
print "ctrl+c!!!!"
else:
oldSignal()
oldSignal = signal.signal(signal.SIGINT, handler)
答案 6 :(得分:-2)
只需在课程Console(cmd.Cmd)
中添加:
def cmdloop(self):
try:
cmd.Cmd.cmdloop(self)
except KeyboardInterrupt as e:
self.cmdloop()
忘记所有其他的东西。这有效。它不会在循环内部形成循环。当它捕获KeyboardInterrupt
时,它会调用do_EOF
,但只会执行第一行;因为do_EOF
中的第一行返回do_exit
,这很好
do_exit
来电postloop
但是,它再次只执行cmd.Cmd.postloop(self)
之后的第一行。在我的程序中,这是打印“\ n”。奇怪的是,如果你SPAM ctrl + C你最终会看到它打印第二行通常只打印在ACTUAL出口(ctrl + Z然后输入,或输入退出)。
答案 7 :(得分:-2)
我更喜欢信号方法,但只是通过。不关心在shell环境中提示用户任何内容。
import signal
def handler(signum, frame):
pass
signal.signal(signal.SIGINT, handler)