readline 由 ANSI 转义序列搞糊涂了。

5

我正在编写一个Python脚本,有时需要用户输入。我在macOS Sierra上使用bash。

为了接收输入,我编写了以下代码:

import readline  # gnureadline 6.3.8

START = '\033[91m\033[1m'
END = '\033[0m'

response = raw_input(START + 'Enter text: ' + END)

我正在使用ANSI转义序列在STARTEND来区分脚本提示和用户文本输入的视觉效果。

不幸的是,一旦我开始输入文本,程序就会失去跟踪用户输入文本开头位置的功能。下面的第一个尖角符号是现在CTRLA映射到的位置,第二个尖角符号是CTRLE带我到的位置。箭头键同样认为用户输入的开头和结尾有偏移,如所示。

Enter text: hello my name is             
                         ^               ^  

我尝试了几种方法来调试这个问题。

  1. 我尝试在提示和文本输入之间加入一个换行符,但这只会使偏移更严重。

  2. 如果我仅使用箭头键而不使用像CTRLACTRLE、选项-箭头等导航字符串的功能,那么这将按预期工作。尽管如此,考虑到用户输入的字符串的长度和复杂性,仅使用箭头键来移动字符串对于用户来说将是痛苦的。

  3. 最重要的是,如果我根本不使用STARTEND,这将完美地工作。尽管如此,脚本的可用性会降低——在完整版本的脚本中,很难从所有其他文本中挑选出提示。

有没有什么诀窍让我能够使用ANSI转义序列格式化提示,而不会影响用户使用CTRLACTRLE和选项-箭头进行导航的能力?


如果你执行 print(START + '输入文本:' + END, end='') 然后 response = raw_input() 会怎样呢?我现在无法尝试,只是一个想法。 - ndmeiri
Readline是否支持Bash的$PS1样式标记非打印序列的\[\]?顺便说一句,最好使用tput来获取可移植终端代码 - 并非每个终端都是VT220! - Toby Speight
@ndmeiri,“end =''”语法在我的Python 2.7.15中无效,但将提示和“raw_input”分开成两个单独的调用似乎可以解决问题(尽管会引入一个换行符)。谢谢! - TAH
1
@TAH 很好。我会扩展并将其作为答案添加。 - ndmeiri
1
\[\] 是特定于提示符的,但它们表示 Readline 通常使用的 \001\002(如果我没记错的话)。 - chepner
然而,我认为Readline是否相关取决于您运行脚本的环境。Python本身并没有使用它。 - chepner
3个回答

4

Readline期望在不可见字符周围使用\001\002作为标记:

import readline  # gnureadline 6.3.8

START = '\001\033[91m\033[1m\002'
END = '\001\033[0m\002'

response = raw_input(START + 'Enter text: ' + END)

使用以 \001 为前缀,以 \002 为后缀的 ANSI 转义序列,GNU readline 将知道这些字符在确定提示字符串长度时应该被排除。


0

将提示信息和输入命令分开。

Python 3.x

print(START + 'Enter text: ' + END, end='')
response = input()

Python 2.x

import sys

# Use sys.stdout.write to avoid printing a trailing newline.
sys.stdout.write(START + 'Enter text: ' + END)
response = raw_input()

感谢您添加有关避免尾随换行符的提示! - TAH
您可以在Python 2中使用print ...,;来抑制换行:print ...,; response = raw_input() - Davis Herring

-1
在我的情况下,被采纳的答案并没有起作用。
我从以下内容开始:
START = '\x1b[7m\x1b[37m' # white on black
END = '\x1b[0m'  # reset
response = input(START + 'QUERY ' + END)

同样导致了

QUERY hello my name is hello my name is
              ^                                ^
              hello my name is hello my name is

使用上述解决方案,

print(START + 'QUERY ' + END, end='')
response = input()

导致它认为该行从开头开始

QUERY hello my name is hello my name is
^                                ^
hello my name is hello my name is

可能是由于input("")长度为0个字符。

知道这些控制字符被计算为偏移量,我们可以再次使用它们来创建偏移量。

我的文本QUERY 长度为6个字符,所以我可以使用

print(START + 'QUERY ' + END, end='')
response = input('\033[D\033[C')

因为 光标向前,光标向后 '\033','[','D''\033','[','C' 这会欺骗readline并偏移6个字符。

QUERY hello my name is hello my name is
\[D\[C^                                ^
      hello my name is hello my name is

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接