Better handling of KeyboardInterrupt in cmd.Cmd command line interpreter
使用python的cmd.Cmd创建自定义CLI时,如何告诉处理程序中止当前行并给我一个新提示?
这是一个最小的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | # 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。我遇到了同样的问题,我找到了解决方案。
这是代码:
1 2 3 4 5 6 7 8 9 10 11 | 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(aka CTRL-C)处理程序。
您可以只捕获
在while循环中运行对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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(' ') console = Console() console.cmdloop_with_keyboard_interrupt() print 'Done!' |
执行CTRL-c只会在新行上打印新提示。
1 2 3 4 5 6 7 8 9 10 11 12 13 | (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! |
我想做同样的事情,所以我搜索它并创建基本脚本以便理解,我的代码可以在我的GitHub上找到
所以,在你的代码中,
而不是这个
1 2 3 | if __name__ == '__main__': console = Console() console.cmdloop() |
用这个,
1 2 3 4 5 6 7 | if __name__ == '__main__': console = Console() try: console.cmdloop() except KeyboardInterrupt: print ("print sth.") raise SystemExit |
希望这会帮助你。好吧,它对我有用。 ??
回答此答案中的以下评论:
This appears to be converging on a solution, but I don't know how to integrate it into my own code (above). I have to figure out the 'super' line. I need to try and get this working at some point in the future.
如果除了
1 | class Console(cmd.Cmd, object): |
您可以查看
1 2 3 4 5 6 7 8 9 10 11 12 | import signal oldSignal = None def handler(signum, frame): global oldSignal if signum == 2: print"ctrl+c!!!!" else: oldSignal() oldSignal = signal.signal(signal.SIGINT, handler) |
您可以使用信号处理程序捕获CTRL-C信号。如果你添加下面的代码,解释器拒绝退出CTRL-C:
1 2 3 4 5 6 | import signal def handler(signum, frame): print 'Caught CTRL-C, press enter to continue' signal.signal(signal.SIGINT, handler) |
如果您不希望在每个CTRL-C之后按ENTER键,只需让处理程序不执行任何操作,这将捕获信号而不会产生任何影响:
1 2 | def handler(signum, frame): """ just do nothing""" |
我更喜欢信号方法,但只是通过。不关心在shell环境中提示用户任何内容。
1 2 3 4 5 6 | import signal def handler(signum, frame): pass signal.signal(signal.SIGINT, handler) |
只需在类
1 2 3 4 5 | def cmdloop(self): try: cmd.Cmd.cmdloop(self) except KeyboardInterrupt as e: self.cmdloop() |
忘记所有其他的东西。这有效。它不会在循环内部形成循环。当它捕获
但是,它再次只执行