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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7907827/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 01:00:09  来源:igfitidea点击:

How can I get (and set) current bash cursor position when using python readline?

pythonbashreadline

提问by Jim Cortez

I have a python script that takes manages the stdin, stdout, and stderr of any application and allows for readline to be inserted gracefully. Think of any application that has lots of console output, but also accepts commands from stdin.

我有一个 python 脚本,它管理任何应用程序的 stdin、stdout 和 stderr,并允许正常插入 readline。想想任何具有大量控制台输出但也接受来自标准输入的命令的应用程序。

In any case, my script uses these two functions:

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

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()

When handling stdout, I call print_line(). This blanks whatever the user might be typing, prints the line, then restores the user's input text. This all happens without the user noticing a thing.

在处理标准输出时,我调用了 print_line()。这将清除用户可能正在输入的任何内容,打印该行,然后恢复用户的输入文本。这一切都发生在用户没有注意到的情况下。

The problemoccurs when the cursor is not at the end of whatever input the user is typing. When the cursor is in the middle of the test and a line is printed, the cursor will automatically be placed at the end of the input. To solve this, I want to do something like this in 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()

Edit: To try and visualize what I have written:

编辑:尝试可视化我所写的内容:

The terminal looks like this:

终端看起来像这样:

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

So the output text is constantly scrolling as new logs are coming through. At the same time, the user is currently editing and writing a new command that will be inserted once the hit enter. So it looks like the python console, but with output always being appended.

因此,随着新日志的通过,输出文本会不断滚动。同时,用户当前正在编辑和编写一个新命令,该命令将在点击回车后插入。所以它看起来像 python 控制台,但总是附加输出。

回答by sehe

Might I suggest Python curses?

我可以建议Python 诅咒吗?

Here is the Basic how-to

这是基本的操作方法

The curses module provides an interface to the curses library, the de-facto standard for portable advanced terminal handling.

While curses is most widely used in the Unix environment, versions are available for DOS, OS/2, and possibly other systems as well. This extension module is designed to match the API of ncurses, an open-source curses library hosted on Linux and the BSD variants of Unix.

curses 模块提供了一个到 curses 库的接口,curses 库是便携式高级终端处理的事实上的标准。

虽然curses 在Unix 环境中使用最广泛,但也有适用于DOS、OS/2 和其他系统的版本。该扩展模块旨在匹配 ncurses 的 API,这是一个托管在 Linux 上的开源 Curses 库和 Unix 的 BSD 变体。



Alternatively

或者

I found Terminal Controller here: Using terminfo for portable color output & cursor control. It looks to be more portable than the sitename would suggest (MacOS mentioned in the comments - though with changes).

我在这里找到了终端控制器:使用 terminfo 进行便携式颜色输出和光标控制。它看起来比站点名称所建议的更便携(评论中提到了 MacOS - 尽管有变化)。

Here is a usage example, displaying a progress bar:

这是一个使用示例,显示一个进度条:

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