pexpect是一个用python实现的类似expect的库,支持ssh,ftp等协议的交互。
以前自己写了一篇使用expect+python拉取数据的文章,现在发现了pexpect,便想要完全用python来写。总觉得用expect生成中间文件不是很好。
大致了解了下pexpect的用法后,发现不是很难,方法和expect相比也是类似的,比如spawn。不过在使用过程中,发现了一个很大的问题:使用pexpect spawn luit -encoding GBK ssh user@server(luit是用来解决ssh非UTF-8编码的简单方法),退出脚本之后ssh进程并没有退出。这个问题搞了我好几天,最后我写了一个简单的work around,等会儿可以看到。
为了查明原因,我在使用pexpect进入interact模式后,用pstree -p查看了下进程关系,顶层的python关联luit,luit关联ssh。但是脚本退出之后,luit退出了,但是ssh没有,父进程变成了init(1)。另一方面,假如我不使用luit直接用ssh的话是没有这种问题的,因为python直接关联ssh,退出脚本时ssh也退出了。
解决方法有多个,比如直接用ssh,但是需要额外处理编码问题(怨念的非UTF-8服务器编码),第二种是在退出时额外处理一下luit下的ssh子进程。
第一种方法我尝试了LANG环境变量等,貌似没起效果。在pexpect上做一层编码转换的话估计也是可行的,但是复杂度很高。不得已考虑第二种方案。我看了下,pexpect直接kill的pid是luit的进程id,不是ssh的,所以问题就变成了如何根据父进程id查找子进程id和如何kill掉子进程。后者很简单,os.kill即可,但是前者花了我一些时间,后来还是考虑用shell命令实现,貌似没有直接的系统调用。shell如下:
ps -o pid --noheaders --ppid %d
%d就是父进程id,这里就是luit的pid。
最后在前面的方案的基础上我写了一个相对完整的一个封装pessh(注意不是pxssh),支持非UTF-8编码,希望对各位有用。