使用Python子进程将stdout重定向到stdin?

发布于 2024-12-20 19:36:10 字数 2371 浏览 2 评论 0原文

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

我使用 Popen() 调用程序,然后想将流传递给 Python 包(称为“pysam”)中的函数,不幸的是,该函数无法使用 Python 文件对象,但可以从 STDIN 读取。所以我想做的是将 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 文件对象读取和写入,但它支持从 stdin 和 stdout 读取和写入。以下示例从 stdin 读取并写入 stdout:

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

它也适用于 BAM 文件。以下脚本将 stdin 上的 BAM 格式文件转换为 stdout 上的 SAM 格式文件:

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

注意,只需将文件打开模式从 r 更改为 rb。 """

所以我只想获取来自 Popen 的流,它读取 stdout,并将其重定向到 stdin,这样我就可以使用 Samfile("-", "rb"),如上面部分所述是可能的。

谢谢。

I'm making a call to a program from the shell using the subprocess module that outputs a binary file to STDOUT.

I use Popen() to call the program and then I want to pass the stream to a function in a Python package (called "pysam") that unfortunately cannot Python file objects, but can read from STDIN. So what I'd like to do is have the output of the shell command go from STDOUT into STDIN.

How can this be done from within Popen/subprocess module? This is the way I'm calling the shell program:

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

This will read "my_cmd"'s STDOUT output and get a stream to it in p. Since my Python module cannot read from "p" directly, I am trying to redirect STDOUT of "my_cmd" back into STDIN using:

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

I then call my module, which uses "-" as a placeholder for STDIN:

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

The above call just means read from STDIN (denoted "-") and read it as a binary file ("rb").

When I try this, I just get binary output sent to the screen, and it doesn't look like the Samfile() function can read it. This occurs even if I remove the call to Samfile, so I think it's my call to Popen that is the problem and not downstream steps.

EDIT: In response to answers, I tried:

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__    

This seems to hang. I get the output:

Opening SAM..

but it never gets past the Samfile("-", "rb") line. Any idea why?

Any idea how this can be fixed?

EDIT 2: I am adding a link to Pysam documentation in case it helps, I really cannot figure this out. The documentation page is:

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

and the specific note about streams is here:

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

In particular:

"""
Pysam does not support reading and writing from true python file objects, but it does support reading and writing from stdin and stdout. The following example reads from stdin and writes to stdout:

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

It will also work with BAM files. The following script converts a BAM formatted file on stdin to a SAM formatted file on stdout:

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

Note, only the file open mode needs to changed from r to rb.
"""

So I simply want to take the stream coming from Popen, which reads stdout, and redirect that into stdin, so that I can use Samfile("-", "rb") as the above section states is possible.

thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

魂牵梦绕锁你心扉 2024-12-27 19:36:10

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

例如:

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 解释器的 stdin。不幸的是,事实并非如此;当指定“-”时,它直接从文件描述符 0 读取。

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

将该输出获取到文件描述符 0 的唯一真正方法是将其移动到另一个脚本并将两个脚本从第一个脚本连接在一起。这确保了第一个脚本中 Popen 的输出将最终出现在第二个脚本的文件描述符 0 上。

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

I'm a little confused that you see binary on stdout if you are using stdout=subprocess.PIPE, however, the overall problem is that you need to work with sys.stdin if you want to trick pysam into using it.

For instance:

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

UPDATE: This assumed that pysam is running in the context of the Python interpreter and thus means the Python interpreter's stdin when "-" is specified. Unfortunately, it doesn't; when "-" is specified it reads directly from file descriptor 0.

In other words, it is not using Python's concept of stdin (sys.stdin) so replacing it has no effect on pysam.Samfile(). It also is not possible to take the output from the Popen call and somehow "push" it on to file descriptor 0; it's readonly and the other end of that is connected to your terminal.

The only real way to get that output onto file descriptor 0 is to just move it to an additional script and connect the two together from the first. That ensures that the output from the Popen in the first script will end up on file descriptor 0 of the second one.

So, in this case, your best option is to split this into two scripts. The first one will invoke my_cmd and take the output of that and use it for the input to a second Popen of another Python script that invokes pysam.Samfile("-", "rb").

烈酒灼喉 2024-12-27 19:36:10

在处理 pysam 的具体情况下,我能够使用命名管道(http://docs.python.org/library/os.html#os.mkfifo)来解决该问题,这是一个可以像普通文件一样访问。一般来说,您希望管道的使用者(读取器)在开始写入管道之前进行侦听,以确保您不会错过任何内容。但是,如果标准输入上尚未注册任何内容,则 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)

In the specific case of dealing with pysam, I was able to work around the issue using a named pipe (http://docs.python.org/library/os.html#os.mkfifo), which is a pipe that can be accessed like a regular file. In general, you want the consumer (reader) of the pipe to listen before you start writing to the pipe, to ensure you don't miss anything. However, pysam.Samfile("-", "rb") will hang as you noted above if nothing is already registered on stdin.

Assuming you're dealing with a prior computation that takes a decent amount of time (e.g. sorting the bam before passing it into pysam), you can start that prior computation and then listen on the stream before anything gets output:

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)
身边 2024-12-27 19:36:10

如果您的系统支持;您可以使用/dev/fd/#文件名

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

If your system supports it; you could use /dev/fd/# filenames:

process = subprocess.Popen(args, stdout=subprocess.PIPE)
samfile = pysam.Samfile("/dev/fd/%d" % process.stdout.fileno(), "rb")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文