[英]Better handling of KeyboardInterrupt in cmd.Cmd command line interpreter
使用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模块创建自己的命令行解释器来管理与某些软件的交互。 我经常按ctrl + c,期望返回新提示的流行的类似shell的行为,而不会对输入的任何命令起作用。 然而,它只是退出。 我试图在代码中的各个点捕获KeyboardInterrupt异常(preloop等)无济于事。 我已经阅读了一些关于sigints的内容,但不太清楚这里是如何适应的。
更新:从下面的建议,我试图实现信号,并能够这样做,使ctrl + c现在不退出CLI,但打印消息。 但是,我的新问题是我似乎无法告诉处理程序函数(见下文)在打印之外做很多事情。 我想ctrl + c基本上中止当前行并给我一个新的提示。
我目前正在使用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 )处理程序。
您可以只捕获cmd.Cmd.cmdloop()
引发的KeyboardInterrupt
,而不是使用信号处理。 您当然可以使用信号处理,但这不是必需的。
在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!
回答此答案中的以下评论:
这似乎是在解决方案上趋同,但我不知道如何将它集成到我自己的代码中(上图)。 我必须弄清楚'超级'线。 我需要尝试在将来的某个时候开展这项工作。
如果你有除cmd.Cmd
之外的类扩展object
, super()
将在这个答案中工作。 像这样:
class Console(cmd.Cmd, object):
我想做同样的事情,所以我搜索它并创建基本脚本以便理解,我的代码可以在我的GitHub上找到
所以,在你的代码中,
而不是这个
if __name__ == '__main__':
console = Console()
console.cmdloop()
用这个,
if __name__ == '__main__':
console = Console()
try:
console.cmdloop()
except KeyboardInterrupt:
print ("print sth. ")
raise SystemExit
希望这会帮助你。 好吧,它对我有用。 😀
您可以查看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)
只需在类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然后输入,或输入退出)。
我更喜欢信号方法,但只是通过。 不关心在shell环境中提示用户任何内容。
import signal
def handler(signum, frame):
pass
signal.signal(signal.SIGINT, handler)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.