2017-04-22 48 views
2

我试图运行一个基于控制台的游戏(地牢爬行石汤 - 用于研究目的自然)多个实例使用多处理池来评估每次运行。使用Python子进程处理交互式shell

在过去,当我使用池来评估相似的代码(遗传算法)时,我用subprocess.call来分割每个进程。但是,由于dcss具有相当的交互性,因此共享子shell似乎有问题。

我有我通常用于这种事情的代码,用抓取代替我引发GA的其他应用程序。有没有比这更好的方式来处理高度交互的shell?我曾考虑过为每个实例开辟一个屏幕,但认为有一个更清晰的方法。我的理解是shell=True应该产生一个子外壳,但我想它是在每次调用之间共享的方式产生的。

我应该提到我有一个机器人运行游戏,所以我不希望任何实际的交互从用户的一端发生。

# Kick off the GA execution 
pool_args = zip(trial_ids,run_types,self.__population) 
pool.map(self._GAExecute, pool_args) 

--- 

# called by pool.map 
def _GAExecute(self,pool_args): 
    trial_id  = pool_args[0] 
    run_type  = pool_args[1] 
    genome   = pool_args[2] 
    self._RunSimulation(trial_id) 

# Call the actual binary 
def _RunSimulation(self, trial_id): 
    command = "./%s" % self.__crawl_binary 
    name = "-name %s" % trial_id 
    rc  = "-rc %s" % os.path.join(self.__output_dir,'qw-%s'%trial_id,"qw -%s.rc"%trial_id) 
    seed = "-seed %d" % self.__seed 
    cdir = "-dir %s" % os.path.join(self.__output_dir,'qw-%s'%trial_id) 

    shell_command = "%s %s %s %s %s" % (command,name,rc,seed,cdir) 
    call(shell_command, shell=True) 

回答

1

确实是可以关联stdin和stdout到文件,从@napuzba了答案:

fout = open('stdout.txt','w') 
ferr = open('stderr.txt','w') 
subprocess.call(cmd, stdout=fout, stderr=ferr) 

另一种选择是使用Popen而不是call。不同的是,呼叫等待完成(阻止),而Popen不是,请参阅What's the difference between subprocess Popen and call (how can I use them)?

使用Popen,然后可以将stdout和stderr保留在对象中,然后稍后使用它们,而不必依赖文件:

p = subprocess.Popen(cmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
p.wait() 
stderr = p.stderr.read() 
stdout = p.stdout.read() 

这种方法的另一个潜在的好处是,你可以运行POPEN的多个实例,而无需等待完成,而不必一个线程池:

processes=[ 
    subprocess.Popen(cmd1,stdout=subprocess.PIPE, stderr=subprocess.PIPE), 
    subprocess.Popen(cmd2,stdout=subprocess.PIPE, stderr=subprocess.PIPE), 
    subprocess.Popen(cmd3,stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
] 

for p in processes: 
    if p.poll(): 
    # process completed 
    else: 
    # no completion yet 

在一个侧面说明,你应该如果可以的话,3210 shell=True,如果你不使用它,Popen希望列表作为一个命令而不是一个字符串。不要手动生成此表,但使用shlex将采取所有角落的情况下为照顾你,例如:

Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
+0

这是有帮助的,但我很确定我需要线程池阻塞。它在GA中运行,因此在下一次控制循环迭代被调用之前,必须完成所有群体成员的评估。 – espais

+1

由于for循环中的poll(),它可以被阻塞。这个解决方案的好处是你并不需要自己管理线程/线程池来启动子进程的运行。 – Guillaume

1

指定标准输入,标准输出和标准错误与独特的文件句柄每个呼叫:

import subprocess 
cmd = "" 
fout = open('stdout.txt','w') 
fin = open('stdin.txt','r') 
ferr = open('stderr.txt','w') 
subprocess.call(cmd, stdout=fout , stdin = fin , stderr=ferr) 
+0

我真的可以用它来捕捉ttyrec? – espais

+0

那么,我不熟悉ttyrec。但是,您可以从'fin'提供输入,它可以是文件对象或任何类似文件的对象。 – napuzba

+0

如果你在linux下工作 - 你可以尝试使用/ dev/tty *文件 – napuzba