在Python中链接子进程
你好,我有个问题想问关于在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 个回答
是的,你可以使用管道功能。看看你是否能从标准输入(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)
请查看文档中的替换 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 信号。
这里其实不需要用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,我强烈建议你学习如何做到这一点:
- 不完全依赖shell,
- 不生成需要正确转义的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()