您期望的是一个接收命令作为输入并返回的函数有意义的通过运行命令输出。
由于命令是任意的,对 tty 的要求只是可能发生的许多不良情况之一(其他包括运行无限循环),您的函数应该只关心它的运行周期,换句话说,命令是“坏”还是不应该取决于它是否在有限时间内结束,并且因为subprocess
本质上是异步的,您可以只运行命令并在更高的视野中处理它。
演示代码播放,可以更改cmd
值来看看它的表现有何不同:
#!/usr/bin/env python
# coding: utf-8
import time
import subprocess
from subprocess import PIPE
#cmd = ['ls']
#cmd = ['sleep', '3']
cmd = ['vim', '-u', '/dev/null']
print 'call cmd'
p = subprocess.Popen(cmd, shell=True,
stdin=PIPE, stderr=PIPE, stdout=PIPE)
print 'called', p
time_limit = 2
timer = 0
time_gap = 0.2
ended = False
while True:
time.sleep(time_gap)
returncode = p.poll()
print 'process status', returncode
timer += time_gap
if timer >= time_limit:
print 'timeout, kill process'
p.kill()
break
if returncode is not None:
ended = True
break
if ended:
print 'process ended by', returncode
print 'read'
out, err = p.communicate()
print 'out', repr(out)
print 'error', repr(err)
else:
print 'process failed'
上述代码中值得注意的三点:
We use Popen
代替check_output
运行命令,与check_output
这将等待该过程结束,Popen
立即返回,因此我们可以做进一步的事情来控制过程。
我们实现一个计时器来检查进程的状态,如果它运行太长时间,我们手动杀死它,因为我们认为如果一个进程不能在有限的时间内结束,它就没有意义。这样你原来的问题就解决了,如下vim
永远不会结束,它肯定会被作为一个“无意义”的命令而被杀死。
定时器帮助我们过滤掉坏命令后,我们可以通过调用来获取命令的stdout和stderrcommunicate
的方法Popen
对象,之后您可以选择确定返回给用户的内容。
结论
不需要tty模拟,我们应该异步运行子进程,然后通过定时器控制它来确定是否应该杀死它,对于正常结束的,它是安全且容易获得输出的。