2013-08-08 2 views
3

Я работал с трубами и IO.popen конкретно в Ruby и столкнулся с проблемой, которую я не могу понять. Я пытаюсь записать двоичные данные из процесса flac в процесс lame в файл. Структура кода, которую я использую, приведен ниже.ruby ​​- IO.popen не работает lame stdin и stdout encoding

# file paths 
file = Pathname.new('example.flac').realpath 
dest = Pathname.new('example.mp3') 

# execute the process and return the IO object 
wav = IO.popen("flac --decode --stdout \"#{file}\"", 'rb') 
lame = IO.popen("lame -V0 --vbr-new - -", 'r+b') 

# write output from wav to the lame IO object 
lame << wav.read 

# close pipe for writing (should indicate to the 
# process that input for stdin is finished). 
lame.close_write 

# open up destiniation file and write from lame stdout 
dest.open('wb'){|out| 
    out << lame.read 
} 

# close all pipes 
wav.close 
lame.close 

Однако, это не работает. После запуска flac скрипт зависает и lame остается бездействующим (без использования процессора вообще). Ошибок и ошибок не возникает.

Я использую cygwin для Windows 7 с пакетом ruby ​​cygwin (1.9.3p429 (2013-05-15) [i386-cygwin]).

Я должен делать что-то неправильно, любая помощь очень ценится. Благодаря!

EXTRA # 1

Я хотел трубы и из двоичных данных из lame процесса, потому что я пытаюсь создать платформу независимой (поддержки рубина ограничена конечно) перекодировать аудиофайлы , а двоичный код Windows lame поддерживает только имена путей Windows, а не cygwin.

EDIT # 1

Я читал в некоторых местах (я не сохранить URL, я буду стараться искать их в моей истории браузера), что IO.popen имеет проблемы с блокировкой процессов в Windows, и что это может быть так.

я играл с другими библиотеками, включая Руби Open3.popen3 и Open4, однако после очень похожего кода структуры на один выше, процесс lame до сих пор висит и остается отвечать на запросы.

EDIT # 2

Я нашел this статью, в которой говорил об ограничениях в ОС Windows на cmd.exe и как он предотвращает использование потоковых данных из файлов на стандартный ввод.

Я отредактировал свой код, как показано ниже, чтобы проверить это, и, как оказалось, lame зависает на записи stdin. Если я удалил (закомментировал) эту строку, процесс lame выполнит (с предупреждением «неподдерживаемый звуковой формат»). Возможно, что сказанная статья может объяснить мою проблему здесь.

# file paths 
file = Pathname.new('example.flac').realpath 
dest = Pathname.new('example.mp3') 

# some local variables 
read_wav = nil 
read_lame = nil 

# the flac process, which exits succesfully 
IO.popen("flac --decode --stdout \"#{file}\"", 'rb'){|wav| 
    until wav.eof do 
     read_wav = wav.read 
    end 
} 

# the lame process, which fails 
IO.popen("lame -V0 --vbr-new --verbose - -", 'r+b'){|lame| 
    lame << read_wav # if I comment out this, the process exits, instead of hanging 
    lame.close_write 
    until lame.eof do 
     read_lame << lame.read 
    end 
} 

EDIT # 3

Я нашел stackoverflow, который (в первом ответе) упоминается, что cygwin реализация трубы ненадежно. Возможно, это не может быть связано с Windows (по крайней мере, не напрямую), а вместо этого с cygwin и его эмуляцией. Вместо этого я решил использовать следующий код, основанный на ответе icy, который работает!

flac = "flac --decode --stdout \"#{file}\"" 
lame = "lame -V0 --vbr-new --verbose - \"#{dest}\"" 

system(flac + ' | ' + lame) 
+0

отредактирован, чтобы попробовать 'Open3 # capture3' и установить': stdin_data'. Или это то же самое, что 'lame << read_wav'? – icy

ответ

2

Вы испытали трубку | персонаж?
Испытано это на окнах с рубином установщиком

require 'open3' 

command = 'dir /B | sort /R' # a windows example command 
Open3.popen3(command) {|stdin, stdout, stderr, wait_thr| 
    pid = wait_thr.pid 
    puts stdout.read #<a list of files in cwd in reverse order> 
} 

Другие способы: Ruby pipes: How do I tie the output of two subprocesses together?

EDIT: с помощью IO::pipe

require 'open3' 

command1 = 'dir /B' 
command2 = 'sort /R' 

reader,writer = IO.pipe 
Open3.popen3(command1) {|stdin, stdout, stderr, wait_thr| 
    writer.write stdout.read 
} 
writer.close 

stdout, stderr, status = Open3.capture3(command2, :stdin_data => reader.read) 
reader.close 

puts "status: #{status}" #pid and exit code 
puts "stderr: #{stderr}" #use this to debug command2 errors 
puts stdout 

вложении два также, кажется, работает, но, как блог, на который вы ссылались, нужно дождаться завершения первой команды (не в режиме реального времени с помощью команды ping)

stdout2 = '' 
Open3.popen3(command1) {|stdin, stdout, stderr, wait_thr| 
    stdout2, stderr2, status2 = Open3.capture3(command2, :stdin_data => stdout.read) 
} 
puts stdout2 
+0

Привет @icy, спасибо за ответ. Я пробовал это и знаю, что это работает, однако для моего сценария я намереваюсь, чтобы и код декодера/кодировщика был отдельным. Именно поэтому я пытаюсь сделать трубопровод через рубин, а не на уровне системы. – Hans

+0

Привет @icy, вы запускаете свой скрипт в Windows '' cmd.exe' или под 'cygwin'? – Hans

+0

Привет @icy, ваше первое предложение, использующее исполнение труб OS, отлично работает, спасибо :) – Hans

Смежные вопросы