繁体   English   中英

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

[英]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之外的类扩展objectsuper()将在这个答案中工作。 像这样:

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

希望这会帮助你。 好吧,它对我有用。 😀

您可以使用信号处理程序捕获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 """

您可以查看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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM