在cmd.Cmd命令行解释器中更好地处理KeyboardInterrupt

14 投票
8 回答
11091 浏览
提问于 2025-04-17 09:58

在使用Python的cmd.Cmd模块创建自定义命令行界面时,我该如何让处理程序中止当前输入并给我一个新的提示符呢?

这里有一个简单的例子:

# 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那样返回一个新的提示符,而不执行我输入的命令。然而,它只是退出了。我尝试在代码的不同位置(如preloop等)捕获KeyboardInterrupt异常,但没有成功。我读了一些关于sigint的内容,但不太清楚这在这里如何适用。

更新:根据下面的建议,我尝试实现信号处理,现在ctrl+c不会退出命令行界面,而是打印出一条消息。然而,我的新问题是,我似乎无法让处理函数(见下文)做太多事情,除了打印。我希望ctrl+c基本上能中止当前输入并给我一个新的提示符。

8 个回答

1

你可以用一个叫做CTRL-C信号的东西来控制程序的停止,这个信号可以通过信号处理器来捕捉。如果你加上下面的代码,程序在你按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 """
6

与其使用信号处理,不如直接捕捉 cmd.Cmd.cmdloop() 抛出的 KeyboardInterrupt。当然,你可以使用信号处理,但这并不是必须的。

可以在一个循环里运行 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!
16

我现在正在用Cmd模块创建一个命令行界面。遇到了同样的问题,不过我找到了解决办法。

以下是代码:

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")
...

现在你在这个命令行界面里有了一个合适的键盘中断处理器,也就是按下CTRL-C时的处理方式。

撰写回答