如何在使用python readline时获取(和设置)当前bash光标位置?

9 投票
1 回答
9562 浏览
提问于 2025-04-17 05:03

我有一个Python脚本,它可以管理任何应用程序的标准输入、标准输出和标准错误输出,并且可以优雅地插入读取行。可以想象一下,任何有大量控制台输出的应用程序,同时也接受来自标准输入的命令。

无论如何,我的脚本使用了这两个函数:

def blank_current_readline():
    # Next line said to be reasonably portable for various Unixes
    (rows,cols) = struct.unpack('hh', fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ,'1234'))

    text_len = len(readline.get_line_buffer())+2

    # ANSI escape sequences (All VT100 except ESC[0G)
    sys.stdout.write('\x1b[2K')                         # Clear current line
    sys.stdout.write('\x1b[1A\x1b[2K'*(text_len/cols))  # Move cursor up and clear line
    sys.stdout.write('\x1b[0G')                         # Move to start of line

def print_line(line):
    global cmd_state
    blank_current_readline()
    print line,
    sys.stdout.write(cmd_state["prompt"] + readline.get_line_buffer())
    sys.stdout.flush()

在处理标准输出时,我会调用print_line()。这个函数会清空用户正在输入的内容,打印出一行新的内容,然后再恢复用户的输入文本。整个过程用户根本不会察觉到。

问题出现在光标不在用户输入的末尾时。当光标在输入的中间位置时,如果打印出一行新内容,光标会自动跳到输入的末尾。为了解决这个问题,我想在print_line中做一些类似这样的操作:

def print_line(line):
    global cmd_state
    cursorPos = getCurrentCursorPos() #Doesn't exist
    blank_current_readline()
    print line,
    sys.stdout.write(cmd_state["prompt"] + readline.get_line_buffer())
    sys.stdout.setCurrentCursorPos(cursorPos) #Doesn't exist
    sys.stdout.flush()

编辑:为了更好地展示我写的内容:

终端看起来是这样的:

----------------------------------------------
|                                            |
|                                            |
|   <scolling command output here>           |
|                                            |
|   <scolling command output here>           |
|                                            |
|: <user inputted text here>                 |
----------------------------------------------

所以输出的文本会随着新日志的到来而不断滚动。同时,用户正在编辑并写入一个新命令,一旦按下回车就会插入这个命令。看起来就像Python控制台,但输出内容总是不断添加的。

1 个回答

3

我可以推荐你看看Python的curses库吗?

这里有一个基础使用指南

curses模块提供了一个接口,可以让你使用curses库,这个库是处理终端的标准工具,能让你的程序在不同的系统上运行得更好。

虽然curses主要是在Unix系统中使用,但也有适用于DOS、OS/2等其他系统的版本。这个扩展模块的设计是为了和ncurses兼容,ncurses是一个开源的curses库,主要在Linux和BSD类的Unix系统上使用。


另外

我在这里找到了Terminal Controller:使用terminfo实现可移植的颜色输出和光标控制。看起来它比名字所暗示的更具可移植性(评论中提到过MacOS,不过需要一些修改)。

这里有一个使用示例,展示了一个进度条:

class ProgressBar:
    """
    A 3-line progress bar, which looks like::
    
                                Header
        20% [===========----------------------------------]
                           progress message

    The progress bar is colored, if the terminal supports color
    output; and adjusts to the width of the terminal.
    """
    BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n'
    HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
        
    def __init__(self, term, header):
        self.term = term
        if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
            raise ValueError("Terminal isn't capable enough -- you "
                             "should use a simpler progress dispaly.")
        self.width = self.term.COLS or 75
        self.bar = term.render(self.BAR)
        self.header = self.term.render(self.HEADER % header.center(self.width))
        self.cleared = 1 #: true if we haven't drawn the bar yet.
        self.update(0, '')

    def update(self, percent, message):
        if self.cleared:
            sys.stdout.write(self.header)
            self.cleared = 0
        n = int((self.width-10)*percent)
        sys.stdout.write(
            self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
            (self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) +
            self.term.CLEAR_EOL + message.center(self.width))

    def clear(self):
        if not self.cleared:
            sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
                             self.term.UP + self.term.CLEAR_EOL +
                             self.term.UP + self.term.CLEAR_EOL)
            self.cleared = 1

撰写回答