ruby - IO.popen没有工作lame stdin和stdout编码

时间:2013-08-08 13:18:37

标签: ruby windows cygwin lame flac

我一直在使用管道和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.popen3Open4,但是遵循与上述代码结构非常相似的代码结构,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)

2 个答案:

答案 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 }}。