использование подпроцесса Python для перенаправления стандартного вывода в стандартный ввод? - PullRequest
1 голос
/ 11 декабря 2011

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

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

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

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

Это прочитает вывод STDOUT my_cmd и получит к нему поток в 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, а не в последующих шагах.

РЕДАКТИРОВАТЬ: В ответ на ответы я попытался:

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. Следующий скрипт преобразует файл в формате 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"), так как описанные выше состояния раздела возможны.

спасибо.

Ответы [ 3 ]

2 голосов
/ 21 мая 2012

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

Предполагая, что вы имеете дело с предыдущим вычислением, которое занимает приличное количество времени (например, сортировка 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)
2 голосов
/ 12 декабря 2011

Я немного озадачен тем, что вы видите двоичный файл на 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

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

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

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

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

0 голосов
/ 23 апреля 2016

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

process = subprocess.Popen(args, stdout=subprocess.PIPE)
samfile = pysam.Samfile("/dev/fd/%d" % process.stdout.fileno(), "rb")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...