使用Python子流程处理交互式shell

使用Python子流程处理交互式shell,python,python-2.7,shell,subprocess,python-multiprocessing,Python,Python 2.7,Shell,Subprocess,Python Multiprocessing,我正在尝试使用一个多处理池来运行一个基于控制台的游戏的多个实例(地下城爬行石汤——自然是为了研究目的),以评估每次运行 在过去,当我使用一个池来评估类似的代码(遗传算法)时,我使用subprocess.call来分割每个进程。然而,由于DCS的交互性很强,拥有一个共享的子shell似乎是有问题的 我有我通常用于这类事情的代码,爬网取代了我抛出GA的其他应用程序。有没有比这更好的方法来处理高度交互的shell?我曾考虑过为每个实例启动一个屏幕,但认为有一个更干净的方法。我的理解是,shell=Tr

我正在尝试使用一个多处理池来运行一个基于控制台的游戏的多个实例(地下城爬行石汤——自然是为了研究目的),以评估每次运行

在过去,当我使用一个池来评估类似的代码(遗传算法)时,我使用
subprocess.call
来分割每个进程。然而,由于DCS的交互性很强,拥有一个共享的子shell似乎是有问题的

我有我通常用于这类事情的代码,爬网取代了我抛出GA的其他应用程序。有没有比这更好的方法来处理高度交互的shell?我曾考虑过为每个实例启动一个屏幕,但认为有一个更干净的方法。我的理解是,
shell=True
应该生成一个子shell,但我想它是以一种在每个调用之间共享的方式生成的

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

# 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)

为每个调用指定具有唯一文件句柄的标准输入、标准输出和标准错误:

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 )

您确实可以将stdin和stdout与文件关联,如@napuzba的答案所示:

fout = open('stdout.txt','w')
ferr = open('stderr.txt','w')
subprocess.call(cmd, stdout=fout, stderr=ferr)
另一种选择是使用而不是。区别在于调用等待完成(阻塞),而Popen没有,请参阅

使用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
另一方面,如果可以,您应该
shell=True
,如果不使用它,Popen希望将列表作为命令而不是字符串。不要手动生成此列表,而是使用它来为您处理所有角落案例,例如:

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

我真的可以用它来捕获一个ttyrec吗?我对ttyrec不太熟悉。但是,您可以从
fin
提供输入,它可以是文件对象或任何类似文件的对象。如果您在linux中工作,您可以尝试使用/dev/tty*files我需要关闭句柄还是子进程会处理它?这很有帮助,但我非常确定我需要线程池被阻塞。它在GA中运行,因此在调用控制循环的下一次迭代之前,必须完成对所有总体成员的评估。由于for循环中的
poll()
,它正在/可能被阻塞。这个解决方案的好处是,您实际上不需要自己管理线程/线程池来启动子进程的运行。