Python Shell 中敲击方向键显示「^[[C^[[D」,原因是什么?如何修复?

> 我想知道为什么会出现这样的问题

那你有没有想过,为什么 terminal 那么大,你却只能从一个角落里开始可怜巴巴地一次输入一行命令么……其实两者的答案如果寻根问底的话——假定你来知乎问问题是想寻根问底——是一样的。

一九七〇年代廉价的电子图像显示器出现之前,大型机的终端通常使用电传打字机(teleprinter)来跟用户交互;电传打字机的输出印在纸上,用户输入的字符就像打字机一样是删不掉的,而且用户输入什么就会立刻传给服务器;后来有了以电子显示器为输出设备的终端,一开始也只能一个字符一个字符地给服务端发信息,到七十年代末能一次给服务器发送一整行信息的「智能」终端才成为主流。此时键盘上才有了现代电脑用户觉得司空见惯(拿键盘当钢琴使的 HHKB Pro 2 用户请扭过头去)的方向键。而按下方向键给终端发送的是控制光标移动的命令,这些命令最初的表示方法是转义字串(escape sequence),也就是「我先说几个字符,说完这几个字符之后再说的那个字符请不要当作字面意思理解」。这个字符在 ANSI 标准里面排行第 27(0x1B),它是个看不见的字符,一般写「<esc>」来代表。而 ANSI 的终端机标准里面,光标上、下、右、左移动的指令分别规定为 <esc>[A,<esc>[B,<esc>[C,<esc>[D,其中方括号跟字母之间还可以插入一个数字来表示要移动几位。

你现在使用的终端模拟器,不管是 Terminal.app 还是 iTerm 2,其实都是在用软件模拟当年用硬件实现的终端,并且还在底层帮你把按下的方向键转化为能够用来移动光标的特定转义字串。编一个 C 程序,main 里只有一行 getchar(); 编译运行(或者先在 iTerm 里按 ctrl + v ),再按上下右左,屏幕上都会显示 ^[[A ^[[B ^[[C ^[[D。其中 ^[ 就代表 esc 字符(因为 ctrl + [ 输入的字符是 0x1B)。

而无论 bash、zsh 还是 OS X 系统自备的 Python shell,其实都是在底层将你按下方向键所产生的这三个字符按照 ANSI 标准理解为你要移动光标,并移动光标。这种「把三个字符按照 ANSI 标准理解为你要移动光标」,或者叫做「line editing」,的功能是如此常用,以至于人们写了专门的程序库来做这件事,它叫做 The GNU Readline Library

但问题在于,readline 采用 GPL v3 授权,这一授权要求所有使用 readline 的程序必须开源。在版权法执行得比我国好的国家里,这是个严重的问题,OS X 里面也就因此没有 readline 库。所以,如果你不去弄一份 GNU Readline,<del>virtualenv</del>(注:此处我把 pythonz 和 virtualenv 弄混了。virtualenv 并不能编译 Python,只是创建一份隔离开来的 Python 运行环境)你自己编译的 Python shell 就没有 line editing 功能;而如果你在创建虚拟环境的时候指定了这一自行编译的版本作为该环境的 Python,那么当你切换到那个环境的时候就会出现这样的问题。为了在不带 GNU Readline 的系统下给 Python 程序提供 line editing 功能,Python 提供了pypi.python.org/pypi/re 模块,以内嵌 GNU readline 库。更多信息可以读它的文档。

那么苹果 OS X 自带的 Python shell 是怎样实现 line editing 的呢?是用 BSD 授权的 Editline Library (libedit)

此外,也有可能是 virtualenv 没有正确地处理独立环境里的库——在 pypi.python.org/pypi/vi 一页上搜寻「readline」,可以看到不少都和 OS X 有关,所以应该确定自己在使用 virtualenv 的最新版本。

而 readline 提供的功能,除去按方向键移动光标,还包括比如 ctrl + a 到行首、ctrl + e 到行尾、alt + f 前进一个单词、alt + b 后退一个单词之类;如果觉得 Emacs 讨厌,还可以转用 vi key-binding。如何才能挖掘出 readline 的全部功能,就留给读者做为习题好了。

> 以及如何修复
easy_install readline
(不,别用pip
原发布于 https://www.zhihu.com/question/21518507/answer/18486781