rpysh——Windows Python 命令行也要 readline!
rpysh 是为习惯 Linux 的 Pythoners 在不得不处理 Windows 上的事务时写的远程 shell。
源起
前些天,我尝试了使用 Python 控制 Word。但我对 Windows 下的交互式 Python shell 很不满意。
首先,我尝试的是 cmd.exe 那个黑窗口。太难用了!只有最基本的行编辑、在不知不觉中历史记录被窜改、复制粘贴极其麻烦。补全当然也是没有的。
于是,尝试 IDLE。这家伙我选了「IDLE Classic Unix」,但是能工作的键并不多。比如我刚刚尝试的Ctrl-u就不管用。而Ctrl-p竟然是把光标向上移动,回车才会把那行的内容取到输入命令的那行。这样一来,想再次执行最后一条语句,需要视上条命令输出的行数按几下Ctrl-p。另外,鼠标在窗口内点击后光标会被移开。这样,我使用鼠标从其它窗口切回来时,还得再手动定位光标,极其不爽。至于补全么,太智能了,所以在我输入时不时会出现这种情况:
还有一个问题:我查资料、做笔记、写代码都在 Linux 上,虽然Ctrl-C、Ctrl-V在物理机和虚拟机间能够无缝操作,但比起选中+中键粘贴的 X 主选区还是麻烦多了!
没办法,我只好重拾很久以前的想法——写个程序,在 Linux 上操作,在 Windows 上执行!
——等等!这和 ssh 差不多吗?或者 telnet?
——不不,Cygwin 的 ssh 跑不了 Windows 控制台程序,而且,不还是没 readline 支持么?
实现
毫无疑问是网络通信了。距离上一次不成功的尝试已经过去很久了,我不仅更加了解了code
模块的能力,也知道 Python 命令行补全是怎么回事了。也就是说,Windows 版的 Python 是有补全的接口的,只是没有 readline 的等价物来调用。跑在 Windows 上的服务端要完成以下操作:
- 重写相关方法,把用户数据由标准输入改到从客户端读取
- 标准输出重定向到网络 socket
- 收到客户端的补全请求后,使用
rlcompleter
模块获取补全结果,再回送给客户端
对于第一点,实际上取代code.InteractiveConsole
实例的raw_input
方法就行。它和内建的input()
函数具有相同的输入和输出形式,也就是会接收命令提示符。将这个直接发给客户端好了。
第二点很简单,直接socket.makefile
然后把sys.stdout
指过去。
第三点,为了简单起见,我另开了个线程和 socket,专门用于补全。需要传递的参数和返回值全部 pickle 了扔给对方就是了。
写完这些我才发现,其实我的raw_input
方法和补全函数具有相似的执行逻辑:发送参数到网络,再从网络获取执行结果——也就是远程过程调用呵。
使用方法
rpyshd.py
可选一个参数作为端口号,为方便起见,提供默认值8980
。也是为了方便双击执行起见,我添加了.py
后缀。
rpyshc
相当于telnet
命令了,直接接主机地址和端口号两个参数即可。
缺陷
- 从标准输入读数据时在服务端
- 偶尔提示符出现不及时
- 虽然我实现了Ctrl-C,但是实际上没什么用,因为收到消息时之前的操作肯定已经执行完了