【问题标题】:Python pipeline using GNU Parallel使用 GNU Parallel 的 Python 管道
【发布时间】:2015-11-12 18:39:09
【问题描述】:

我正在尝试在 Python 中围绕 GNU Parallel 编写一个包装器以并行运行命令,但似乎误解了 GNU Parallel 的工作原理、系统管道和/或 python 子进程管道。

基本上我希望使用 GNU Parallel 来处理拆分输入文件,然后在多个主机上并行运行另一个命令。

我将来可以研究一些纯 python 方法来执行此操作,但似乎应该使用 GNU Parallel 轻松实现。

t.py

#!/usr/bin/env python

import sys

print
print sys.stdin.read()
print

p.py

from subprocess import *
import os
from os.path import *

args = ['--block', '10', '--recstart', '">"', '--sshlogin', '3/:', '--pipe', './t.py']

infile = 'test.fa'

fh = open('test.fa','w')
fh.write('''>M02261:11:000000000-ADWJ7:1:1101:16207:1115 1:N:0:1
CAGCTACTCGGGGAATCCTTGTTGCTGAGCTCTTCCCTTTTCGCTCGCAGCTACTCGGGGAATCCTTGTTGCTGAGCTCTTCCCTTTTCGCTCGCAGCTACTCGGGGAATCCTTGTTGCTGAGCTCTTCCCTTTTCGCTCGCAGCTACTCGGGGAATCCTTGTTGCTGAGCTCTTCCCTTT
>M02261:11:000000000-ADWJ7:1:1101:21410:1136 1:N:0:1
ATAGTAGATAGGGACATAGGGAATCTCGTTAATCCATTCATGCGCGTCACTAATTAGATGACGAGGCATTTGGCTACCTTAAGAGAGTCATAGTTACTCCCGCCGTTTACC
>M02261:11:000000000-ADWJ7:1:1101:13828:1155 1:N:0:1
GGTTTAGAGTCTCTAGTCGATAGATCAATGTAGGTAAGGGAAGTCGGCAAATTAGATCCGTAACTTCGGGATAAGGATTGGCTCTGAAGGCTGGGATGACTCGGGCTCTGGTGCCTTCGCGGGTGCTTTGCCTCAACGCGCGCCGGCCGGCTCGGGTGGTTTGCGCCGCCTGTGGTCGCGTCGGCCGCTGCAGTCATCAATAAACAGCCAATTCAGAACTGGCACGGCTGAGGGAATCCGACGGTCTAATTAAAACAAAGCATTGTGATGGACTCCGCAGGTGTTGACACAATGTGATTTT
>M02261:11:000000000-ADWJ7:1:1101:14120:1159 1:N:0:1
GAGTAGCTGCGAGCGAAAAGGGAAGAGCTCAAGGGGAGGAAAAGAAACTAACAAGGATTCCCCGAGTAGCTGCGAGCGAAAAGGGAAGCGCCCAAGGGGGGCAACAGGAACTAACAAGAATTCGCCGACTAGCTGCGACCTGAAAAGGAAAAACCCAAGGGGAGGAAAAGAAACTAACAAGGATTCCCCGAGTAGCTGCGAGCAGAAAAGGAAAAGCACAAGAGGAGGAAACGACACTAATAAGACTTCCCATACAAGCGGCGAGCAAAACAGCACGAGCCCAACGGCGAGAAAAGCAAAA
>M02261:11:000000000-ADWJ7:1:1101:8638:1172 1:N:0:1
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
''')
fh.close()

# Call 1
Popen(['parallel']+args, stdin=open(infile,'rb',0), stdout=open('output','w')).wait()

# Call 2
_cat = Popen(['cat', infile], stdout=PIPE)
Popen(['parallel']+args, stdin=_cat.stdout, stdout=open('output2','w')).wait()

# Call 3
Popen('cat '+infile+' | parallel ' + ' '.join(args), shell=True, stdout=open('output3','w')).wait()

调用 1 和调用 2 产生相同的输出,而调用 3 产生我期望的输出,其中输入文件被拆分并在记录之间包含空行。

我比较好奇 Call 1,2 和 Call 3 有什么区别。

【问题讨论】:

  • 你愿意使用 GNU 并行以外的东西吗?如果您已经在使用 python,fabric 可能是一种更简单的推送数据的方法。其他方法包括使用IPython.parallel 与SSH 集群管理,或celery 分布式任务队列
  • 脚本最终将在 PBS 或 SGE 作业中运行,因此结构可能会运行得最好。目前,我只对调用的不同之处感兴趣。
  • 一定要看看已经包含 PBS 和 SGE 代码的 IPython
  • 后两者似乎都患有Useless Use of cat。第三个额外将命令组装成一个字符串,以便由 shell 解析回参数,当您已经将参数分开时,这同样无用。
  • 我不知道在执行实际的 shell 管道时您实际上可以将 args 列表作为列表提供。我很确定当你在实际的 shell(又名 shell=True)中运行管道时,你必须提供一个参数字符串而不是一个列表。

标签: python pipe gnu-parallel


【解决方案1】:

TL;DR 不要在 shell=False 时引用 ">"

如果您使用shell=True,您可以使用所有shell 的功能,如globbing、I/O 重定向等。您需要引用任何需要从shell 中转义的内容。您可以将整个命令行作为单个字符串传递,shell 会对其进行解析。

unsafe = subprocess.Popen('echo `date` "my files" * >output', shell=True)

使用shell=False,您在幕后没有“秘密”副作用,并且您无法使用任何shell 功能。因此,您需要在 Python 端处理通配符、重定向等。在加号帐户中,您节省了一个(可能重要的)额外过程,您拥有更多控制权,并且您不需要(实际上也不能)引用在涉及 shell 时必须引用的内容。总之,这也更安全,因为您可以准确地看到自己在做什么。

cmd = ['echo']
cmd.append(datestamp())
cmd.append['my files']  # notice absence of shell quotes around string
cmd.extend(glob('*'))
safer = subprocess.Popen(cmd, shell=False, stdout=open('output', 'w+'))

(这仍然略有不同,因为对于现代 shell,echo 是内置的,而现在,我们将执行一个外部实用程序 /bin/echo 或任何具有该名称的可执行文件在您的 PATH 中首先出现。)

现在,回到您的示例,args 中的问题是您引用文字 ">" 作为记录分隔符。当涉及 shell 时,未加引号的右中断会调用重定向,因此要将其指定为字符串,必须对其进行转义或引用;但是当图片中没有外壳时,没有任何东西可以处理(或需要)这些引号,因此要传递文字 > 参数,只需按字面意思传递即可。

除此之外,您的第 1 电话肯定看起来像是要走的路。 (虽然我并不完全相信为在 Perl 中实现的 shell 命令编写 Python 包装器是明智的。我怀疑直接在 Python 中处理一堆并行子进程不会更复杂。)

【讨论】:

    猜你喜欢
    • 2017-09-09
    • 2014-07-19
    • 2019-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多