子进程打开文件和管道awk命令[重复]

Posted

技术标签:

【中文标题】子进程打开文件和管道awk命令[重复]【英文标题】:subprocess open file and pipe awk command [duplicate] 【发布时间】:2017-10-15 18:23:40 【问题描述】:

这是我的输入文件格式:

@SRR2056440.1 1 length=100
TGTAGGTCTGAGCAGCTTGTCCTGGCTGTGTCCATGTCAGAGCAACGGCCCAAGTCTGGGTCTGGGGGGGAAGGTGTCATGGAGCCCCCTACGATTCCCA
+SRR2056440.1 1 length=100
BCBFFFEFHHHHHJJJJJJIJJJJJJJJIJHHIJJIIJJJJJIJJIJJJJJJJJFHIJJJHHHHHHFDDDBDDD>>ACDEDDDDDDDDDDDDDDDDDEDD
@SRR2056440.2 2 length=100
CTGCCGCCACCGCAGCAGCCACAGGCAGAGGAGGACGAGGACGACTGGGAATCGTAGGGGGCTCCATGACACCTTCCCCCCCAGACCCAGACTTGGGCCA
+SRR2056440.2 2 length=100
CCCFFFFFHHHHHJJJJJJJJJJJIJIJIGJGGIGGJIJJEHFEDDDDDDDDDDABDDDDDDDDDDDDDDADDDDDDDDDDDCDDDDDDBBDDCDDBDD@
@SRR2056440.3 3 length=100
TCTGCCGCCACCGCAGCAGCCACAGGCAGAGGAGGACGAGGACGACTGGGAATCGTAGGGGGCTCCATGACACCTTCCCCCCCAGACCCAGACTTGGGCC
+SRR2056440.3 3 length=100
CCCFFFFFHGHHHJJJJJIJJJJJJIJJIJJJIJJIIIGIJ<CDBCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCDDDDDDDDDDDDDDDDDDCDCBDD

这是我要执行的命令:

cat input.fq | awk 'NR%4==2sum+=length($0);nr++;sumsq+=length($0)*length($0)ENDprintf"%.1f\t%.1f\n",sum/nr,sqrt(sumsq/nr-(sum/nr)**2)'

以及命令的输出:

100.0 0.0

我想使用子进程在 python 脚本中执行该命令。我已经做了几次尝试,但我无法弄清楚,这是我最后一次尝试:

awk_comm = r"""'NR%4==2sum+=length($0);nr++;sumsq+=length($0)*length($0)ENDprintf"%.1f\t%.1f\n",sum/nr,sqrt(sumsq/nr-(sum/nr)**2)'"""
cmd = ['cat', 'input.fq', '|', 'awk', awk_comm]
p2 = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
out1, err = p2.communicate()

编辑:

我在输出中看不到任何错误。它卡住了,永远运行。

【问题讨论】:

顺便说一句,cat input.fq | ... 即使在 shell 中也是不好的做法——它会强制您的 awkcat 读取 FIFO,这必然比直接从文件读取要慢;此外,使用直接文件句柄,您可以重新阅读、四处寻找等;但是一个 FIFO 只能从前到后读取一次。 无论如何,当您使用shell=True 传递一个数组时,结果是subprocess.Popen(['sh', '-c']+yourarray, shell=False)。这意味着唯一作为源传递给 shell 解析的是该数组的 第一个元素。 顺便说一句——在使用 shell=True 之前,请务必查看 docs.python.org/2/library/… 中的警告。 【参考方案1】:

这里没有shell=True 的意义。只需设置您的 subprocess.Popen 对象来执行您原本使用 shell 的所有操作:

# the original awk code, with whitespace added for readability
awk_command = r"""
NR%4==2 
  sum+=length($0);
  nr++;
  sumsq+=length($0)*length($0)

END 
  printf "%.1f\t%.1f\n", sum/nr, sqrt(sumsq/nr-(sum/nr)**2)

"""

p2 = subprocess.Popen(
  ['awk', awk_command],
  stdin=open('input.fq', 'r'),  # pass a file handle to input.fq directly on awk's stdin
  stdout=subprocess.PIPE,
  stderr=subprocess.PIPE)
out1, err = p2.communicate()

【讨论】:

非常感谢。这更有意义。但我使用cat 是有原因的。在执行命令之前,我检查文件是否使用 gzip 压缩,如果是,我使用 zcat 打开文件,否则使用 cat 打开文件。我可以使用 open 打开 gzip 文件吗? @cucurbit,我会做什么,请参阅gist.github.com/charles-dyfis-net/…。这样,您将在可能的情况下传递直接文件句柄,或者从gunzip 进行管道传递。 谢谢!如果您从 input_filename:input_file = open('input_filename', 'r') 中删除引号,则效果很好。【参考方案2】:

以下内容对我有用。

>>> awk_comm = r"""cat input.fq | awk 'NR%4==2sum+=length($0);nr++;sumsq+=length($0)*length($0)ENDprintf"%.1f\t%.1f\n",sum/nr,sqrt(sumsq/nr-(sum/nr)**2)'"""
>>> p2 = subprocess.Popen(awk_comm, stdout=subprocess.PIPE,shell=True)
>>> res = p2.communicate()
>>> res
('100.0\t0.0\n', None)

【讨论】:

【参考方案3】:

您可以使用命令模块来实现:

import commands
awk_comm = r"""'NR%4==2sum+=length($0);nr++;sumsq+=length($0)*length($0)ENDprintf"%.1f\t%.1f\n",sum/nr,sqrt(sumsq/nr-(sum/nr)**2)'"""
p1 = commands.getoutput('cat input.fq | awk ' + awk_comm)
print p1

希望对你有帮助

【讨论】:

使用字符串连接形成 shell 命令天生就容易受到 shell 注入攻击。它在这里是不可利用的,因为所有的字符串都是硬编码的,但是如果你想让用户指定 input.fq 的名称,天真的方法将允许通过将任意命令嵌入到该名称中来运行它们。 此外,commands 被明确弃用,取而代之的是 subprocess。你会注意到commands 在 Python 3 中甚至根本不存在。 subprocess 的文档描述了 how to use it in place of commands 感谢@CharlesDuffy 的洞察【参考方案4】:

默认情况下,Python 不使用 shell 来运行命令...但是管道由 shell 评估!你需要通过shell=True:

p2 = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)

【讨论】:

谢谢你,但我试过了,它也不起作用。 @cucurbit 对于shell=True,您能否编辑您的问题以包含 full 错误?? 没有错误...就像永远运行一样卡住...:/ 使用cmd=['cat', ...],这只是运行cat。其他参数传递给/bin/sh,但没有其他任何参数。 (这就是它永远运行的原因:cat 在没有给出参数或提供 EOF 的标准输入时阻止从标准输入读取)。

以上是关于子进程打开文件和管道awk命令[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Linux查看进程打开多少文件描述符命令

管道和命名管道

linux命令 — lsof 查看进程打开那些文件 或者 查看文件给那个进程使用

Perl:当子/管道的文件句柄被别名时,关闭子进程失败

lsof查看进程打开的文件

如何使用python子进程调用在命令中使用管道[重复]