использование подпроцесса Python для перенаправления стандартного вывода в стандартный ввод?

1

Я делаю вызов программы из оболочки, используя модуль подпроцесса, который выводит двоичный файл в STDOUT.

Я использую Popen() для вызова программы, а затем хочу передать поток функции в пакет Python (называемый "pysam"), который, к сожалению, не может содержать объекты Python, но может читать из STDIN. Так что я хотел бы сделать, так это вывод команды оболочки из STDOUT в STDIN.

Как это можно сделать из модуля Popen/subprocess? Так я вызываю программу оболочки:

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

Это будет читать вывод "my_cmd" STDOUT и получить поток к нему в p. Поскольку мой модуль Python не может напрямую читать "p", я пытаюсь перенаправить STDOUT "my_cmd" обратно в 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 - это проблема, а не последующие шаги.

EDIT: В ответ на ответы я попытался:

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"). Любая идея почему?

Любая идея, как это можно исправить?

EDIT 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. Следующий скрипт преобразует отформатированный файл BAM на stdin в форматированный файл SAM на stdout:

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

Обратите внимание, что только режим открытия файла должен изменяться с r на rb. """

Поэтому я просто хочу взять поток из Popen, который читает stdout, и перенаправить это в stdin, так что я могу использовать Samfile ("-", "rb"), так как это может быть в указанных выше разделах.

Благодарю.

  • 0
    import sys , sys.stdin.write(p.stdout.read()) ? Убедитесь, что s = pysam.Samfile("-", "rb") перед записью в stdin ..
  • 0
    Вы все еще видите двоичный вывод на экран? Другими словами, вы уверены, что ваш tagBam_cmd действительно отправляет результат на стандартный вывод?
Показать ещё 4 комментария
Теги:
subprocess
streaming

3 ответа

2
Лучший ответ

Я немного смущен, что вы видите двоичный файл stdout, если используете stdout=subprocess.PIPE, однако общая проблема заключается в том, что вам нужно работать с 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

UPDATE: Предполагается, что pysam работает в контексте интерпретатора Python и, следовательно, означает интерпретатор Python stdin, когда указан параметр "-". К сожалению, это не так; когда указано "-", он считывается непосредственно из дескриптора файла 0.

Другими словами, он не использует концепцию python для stdin (sys.stdin), поэтому ее замена не влияет на pysam.Samfile(). Также невозможно взять вывод из вызова Popen и как-то "надавить" его на дескриптор файла 0; это только для чтения, а другой конец - к вашему терминалу.

Единственный реальный способ получить этот вывод в файловом дескрипторе 0 - просто переместить его в дополнительный скрипт и соединить два вместе с первым. Это гарантирует, что вывод из Popen в первом скрипте закончится на дескриптор файла 0 второго.

Итак, в этом случае ваш лучший вариант - разделить это на два сценария. Первый вызовет my_cmd и выведет его вывод и будет использовать его для ввода второго Popen другого скрипта Python, который вызывает pysam.Samfile("-", "rb").

  • 0
    Хотя я предпочел бы просто писать в sys.stdin (см. Мой комментарий к основному сообщению).
  • 1
    Это не сработает. Вы пытаетесь записать в объект файлового потока, который доступен только для чтения, а не помещать данные для последующего чтения. Теперь, если вы открыли / dev / fd / 0 и написали в него, если может работать.
Показать ещё 9 комментариев
2

В конкретном случае работы с pysam я смог обойти проблему, используя именованный канал (http://docs.python.org/library/os.html#os.mkfifo), который является каналом, который может быть доступ как обычный файл. В общем, вы хотите, чтобы потребитель (читатель) трубы слушал, прежде чем начинать писать в трубу, чтобы вы ничего не пропустили. Тем не менее, pysam.Samfile("-", "rb") будет зависать, как вы отметили выше, если на stdin уже ничего не зарегистрировано.

Предполагая, что вы имеете дело с предварительным вычислением, которое занимает достаточное количество времени (например, сортировка бара перед его передачей в 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)
0

Если ваша система поддерживает его; вы можете использовать /dev/fd/# имена файлов:

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню