【问题标题】:using Python subprocess to redirect stdout to stdin?使用 Python 子进程将标准输出重定向到标准输入?
【发布时间】:2011-12-11 19:48:03
【问题描述】:

我正在使用将二进制文件输出到 STDOUT 的子进程模块从 shell 调用程序。

我使用 Popen() 来调用程序,然后我想将流传递给 Python 包(称为“pysam”)中的一个函数,不幸的是它无法 Python 文件对象,但 可以 读取从标准输入。所以我想做的是让shell命令的输出从STDOUT进入STDIN。

如何在 Popen/subprocess 模块中做到这一点?这是我调用 shell 程序的方式:

p = subprocess.Popen(my_cmd, stdout=subprocess.PIPE, shell=True).stdout

这将读取“my_cmd”的 STDOUT 输出并在 p 中获取一个流。由于我的 Python 模块无法直接从“p”读取,我正在尝试使用以下命令将“my_cmd”的 STDOUT 重定向回 STDIN:

p = subprocess.Popen(my_cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True).stdout

然后我调用我的模块,它使用“-”作为 STDIN 的占位符:

s = pysam.Samfile("-", "rb")

上面的调用只是意味着从 STDIN 读取(表示为“-”)并将其作为二进制文件(“rb”)读取。

当我尝试这个时,我只是将二进制输出发送到屏幕上,看起来 Samfile() 函数无法读取它。即使我删除了对 Samfile 的调用也会发生这种情况,所以我认为问题在于我对 Popen 的调用而不是下游步骤。

编辑:为了回应答案,我尝试了:

sys.stdin = subprocess.Popen(tagBam_cmd, stdout=subprocess.PIPE, shell=True).stdout
print "Opening SAM.."                                                                                            
s = pysam.Samfile("-","rb")
print "Done?"
sys.stdin = sys.__stdin__    

这似乎挂了。我得到了输出:

Opening SAM..

但它永远不会超过 Samfile("-", "rb") 行。知道为什么吗?

知道如何解决这个问题吗?

编辑 2:我正在添加指向 Pysam 文档的链接以防万一,我真的无法弄清楚这一点。文档页面是:

http://wwwfgu.anat.ox.ac.uk/~andreas/documentation/samtools/usage.html

关于流的具体说明在这里:

http://wwwfgu.anat.ox.ac.uk/~andreas/documentation/samtools/usage.html#using-streams

特别是:

""" Pysam 不支持从真正的 python 文件对象读取和写入,但它支持从标准输入和标准输出读取和写入。以下示例从标准输入读取并写入标准输出:

infile = pysam.Samfile( "-", "r" )
outfile = pysam.Samfile( "-", "w", template = infile )
for s in infile: outfile.write(s)

它也适用于 BAM 文件。以下脚本将标准输入上的 BAM 格式文件转换为标准输出上的 SAM 格式文件:

infile = pysam.Samfile( "-", "rb" )
outfile = pysam.Samfile( "-", "w", template = infile )
for s in infile: outfile.write(s)

注意,只有文件打开方式需要从r改为rb。 """

所以我只想获取来自 Popen 的流,它读取标准输出,并将其重定向到标准输入,这样我就可以使用 Samfile("-", "rb"),因为上述部分说明是可能的。

谢谢。

【问题讨论】:

  • import sys, sys.stdin.write(p.stdout.read())?在写入标准输入之前确保s = pysam.Samfile("-", "rb")..
  • 您还在屏幕上看到二进制输出吗?换句话说,您确定您的 tagBam_cmd 确实将结果发送到标准输出?
  • 我没有看到二进制屏幕,我只是看到程序挂起。当我运行 tagBam_cmd 的内容时,它会将其打印到屏幕上,所以我假设它是标准输出。我该如何检查呢?
  • 在 shell 中运行你的 tagBam_cmd 为:tagBam_cmd > test_output 并查看二进制文件是否在文件中结束。如果没有,那么它可能会进入 stderr。
  • 我已经这样做了,它肯定会进入文件。

标签: python shell unix streaming subprocess


【解决方案1】:

如果您使用stdout=subprocess.PIPE,您会在标准输出上看到二进制文件,我有点困惑,但是,总体问题是,如果您想欺骗 pysam 使用它,则需要使用 sys.stdin

例如:

sys.stdin = subprocess.Popen(my_cmd, stdout=subprocess.PIPE, shell=True).stdout
s = pysam.Samfile("-", "rb")
sys.stdin = sys.__stdin__ # restore original stdin

更新:假设 pysam 在 Python 解释器的上下文中运行,因此当指定“-”时意味着 Python 解释器的标准输入。不幸的是,它没有;当指定“-”时,它直接从文件描述符 0 读取。

换句话说,它没有使用 Python 的 stdin (sys.stdin) 概念,因此替换它对 pysam.Samfile() 没有影响。也不可能从 Popen 调用中获取输出并以某种方式将其“推送”到文件描述符 0;它是只读的,另一端连接到您的终端。

将该输出输出到文件描述符 0 的唯一真正方法是将其移动到另一个脚本,然后从第一个脚本将两者连接在一起。这确保了第一个脚本中 Popen 的输出将在第二个脚本的文件描述符 0 上结束。

因此,在这种情况下,您最好的选择是将其拆分为两个脚本。第一个将调用 my_cmd 并获取其输出并将其用作另一个调用 pysam.Samfile("-", "rb") 的 Python 脚本的第二个 Popen 的输入。

【讨论】:

  • 虽然我宁愿只写信给sys.stdin(见我对主帖的评论)。
  • 这行不通。您将尝试写入只读的文件流对象 - 不推送数据以供将来读取。现在,如果你打开 /dev/fd/0 并写信给它,如果可以的话。
  • @DavidK.Hess:我尝试了你的建议,但它似乎挂在 Samfile()... 它对你有用吗?请参阅编辑我的原始帖子。
  • @user248237 看看我的编辑 - 你确定 pyasm 是从解释器的标准输入中读取,还是使用这些参数运行外部程序? pyasm.Samfile 文档的链接也会有所帮助。
  • @DavidK.Hess:添加链接。相关部分在这里:wwwfgu.anat.ox.ac.uk/~andreas/documentation/samtools/…
【解决方案2】:

在处理 pysam 的特定情况下,我能够使用命名管道 (http://docs.python.org/library/os.html#os.mkfifo) 来解决这个问题,它是一个管道可以像普通文件一样访问。通常,您希望管道的使用者(读者)在开始写入管道之前进行监听,以确保您不会错过任何内容。但是,如果 stdin 上没有任何内容,则 pysam.Samfile("-", "rb") 将挂起,如上所述。

假设您正在处理需要大量时间的先前计算(例如,在将 bam 传递到 pysam 之前对其进行排序),您可以开始该先前计算,然后在输出任何内容之前监听流:

import os
import tempfile
import subprocess
import shutil
import pysam

# Create a named pipe
tmpdir = tempfile.mkdtemp()
samtools_prefix = os.path.join(tmpdir, "namedpipe")
fifo = samtools_prefix + ".bam"
os.mkfifo(fifo)

# The example below sorts the file 'input.bam',
# creates a pysam.Samfile object of the sorted data,
# and prints out the name of each record in sorted order

# Your prior process that spits out data to stdout/a file
# We pass samtools_prefix as the output prefix, knowing that its
# ending file will be named what we called the named pipe
subprocess.Popen(["samtools", "sort", "input.bam", samtools_prefix])

# Read from the named pipe
samfile = pysam.Samfile(fifo, "rb")

# Print out the names of each record
for read in samfile:
    print read.qname

# Clean up the named pipe and associated temp directory
shutil.rmtree(tmpdir)

【讨论】:

【解决方案3】:

如果您的系统支持;你可以use /dev/fd/# filenames:

process = subprocess.Popen(args, stdout=subprocess.PIPE)
samfile = pysam.Samfile("/dev/fd/%d" % process.stdout.fileno(), "rb")

【讨论】:

    猜你喜欢
    • 2013-07-16
    • 1970-01-01
    • 2022-12-14
    • 1970-01-01
    • 2013-06-02
    • 2016-02-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多