我一直在使用管道和IO.popen
专门用于Ruby,并遇到了一个我无法弄清楚的问题。我正在尝试将flac
进程中的二进制数据写入lame
进程到文件中。我正在使用的代码结构如下。
# 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
保持空闲状态(根本不使用处理器)。 没有错误或异常发生。
我在Windows 7上使用cygwin,使用cygwin ruby包( 1.9.3p429(2013-05-15)[i386-cygwin] )。
我一定是做错了什么,非常感谢任何帮助。谢谢!
EXTRA#1
我想要从lame
进程输入和输出二进制数据,因为我正在尝试创建一个独立的平台(当然 ruby支持)来转码音频文件,并且lame
的Windows二进制文件仅支持Windows的路径名,而不支持cygwin的。
编辑#1
我在某些地方读过(我没有保存网址,我会尝试在我的浏览器历史记录中查找它们)IO.popen
已知在Windows中阻止进程的问题,而且情况可能如此。
我已经玩过其他库,包括Ruby的Open3.popen3
和Open4
,但是遵循与上述代码结构非常相似的代码结构,lame
进程仍然会挂起并且仍然没有响应。< / p>
编辑#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
}
编辑#3
我发现这个stackoverflow(在第一个答案中)提到cygwin
管道实现不可靠。这可能实际上与Windows无关(至少不是直接),而是与cygwin及其仿真有关。我选择使用以下代码,基于 icy 的回答,可以使用!
flac = "flac --decode --stdout \"#{file}\""
lame = "lame -V0 --vbr-new --verbose - \"#{dest}\""
system(flac + ' | ' + lame)
答案 0 :(得分:2)
您是否尝试过管道|
?
在使用ruby安装程序的窗口上测试了这个
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?
修改强>
使用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
答案 1 :(得分:0)
使用Open3
中的pipeline
:
require "open3"
wavCommand = "flac --decode --stdout \"#{file}\""
lameCommand = "lame -V0 --vbr-new - -"
Open3.pipeline(wavComamnd, lameCommand)
最后一行产生两个进程,并将第一个进程的stdout
连接到第二个进程的stdin
。另外,您可以使用stdin
访问第一个进程的pipeline_w
,或者可以使用stdout
获取最后一个命令的pipeline_r
,也可以使用{{1 }}。