在Python中链接子进程

3 投票
3 回答
1456 浏览
提问于 2025-04-17 11:58

你好,我有个问题想问关于在Python中如何把输入和输出连接起来,特别是涉及到子进程的部分。我想简化我的程序,省去一个步骤的输出,直接把它传递给另一个子进程,而不是先输出到一个文件,然后再打开这个文件来处理。

举个例子,第一个进程使用SAMTOOLS从一个大的bam文件中输出一个特定的染色体。

也就是说,bigfile.bam被读取后,输出的是chromosome22.bam。

接下来的子进程使用BEDTOOLS把chromosome22.bam转换成chromosome22.bed。

也就是说,chromosome22.bam被读取后,输出的是chromosome22.bed。

我想做的是把第一个进程的输出直接传给第二个进程,这样就不需要中间的文件了。

到目前为止,我有这样的代码……

for x in 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,'X','Y':
   subprocess.call("%s view -bh %s %s > %s/%s/%s.bam" % (samtools,bam,x,bampath,out,x), shell=True)

这段代码会生成chromosome[1-22,X,Y].bam文件。但是我能不能避免这个过程,把另一个子进程的命令放在同一个循环里,直接把它们转换成bed文件呢?

用于bed转换的命令是:

bedpath/bedtools bamtobed -i [bamfile] > [bedfile]

3 个回答

0

是的,你可以使用管道功能。看看你是否能从标准输入(stdin)读取数据到bamtobed这个过程……如果可以的话,试试下面的方法。这样做可以节省磁盘输入输出的时间,前提是处理的负担比较轻。

稍微修改一下:

proc1.stdout现在是第二个过程的标准输入(stdin)。

proc1 = subprocess.call("%s view -bh %s %s" % (samtools,bam,x,bampath,out,x), shell=True, stdout=subprocess.PIPE)

proc2 = subprocess.call("bedpath/bedtools bamtobed > %s" % (outFileName, ), shell=True, stdin=proc1.stdout)
4

请查看文档中的替换 shell 管道示例。

output=$(dmesg | grep hda)

变成:

p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]

解释如下:

在启动 p2 之后调用 p1.stdout.close() 是很重要的,这样如果 p2 在 p1 之前退出,p1 就会收到一个 SIGPIPE 信号。

1

这里其实不需要用Python,使用shell会简单很多。不过基本上,它的工作原理和Python是一样的。

如果bedtools可以从标准输入(stdin)读取数据,你可以这样做:

#!/bin/sh
for x in `seq 1 22` X Y; do
   $samtools view -bh $bam $x | $bedtools bamtobed > $bampath/$out/$x.bam
done

根据bedtools的设计,你可能还需要加上-i -这个选项,才能让它从stdin读取数据。

如果你还是想用Python,我强烈建议你学习如何做到这一点:

  1. 不完全依赖shell,
  2. 不生成需要正确转义的shell命令,以避免出错。

使用subprocess会更安全,特别是当你使用基于数组的语法,并且不使用shell时。可以将其分成两个子进程调用,每个命令一个。想了解更多细节,可以查看http://docs.python.org/library/subprocess.html#replacing-shell-pipeline

cmd1 = [samtools, "view", "-bh", bam, x]
cmd2 = [bedtools, "bamtobed"]

c1 = subprocess.Popen(cmd1, stdout=subprocess.PIPE)
c2 = subprocess.Popen(cmd2, stdin=c1.stdout, stdout=open(outputfilename, "w"))
c1.stdout.close()
c2.communicate()

撰写回答