4

我一直在使用管道,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])。

我一定做错了什么,非常感谢任何帮助。谢谢!

额外 #1

我想从lame进程中输入和输出二进制数据,因为我正在尝试创建一个独立于平台(当然是 ruby​​ 支持有限)来转码音频文件,并且 Windows 二进制文件lame仅支持 Windows 的路径名,而不支持 cygwin 的.

编辑#1

我在某些地方(我没有保存 URL,我会尝试在我的浏览器历史记录中查找它们)阅读了IO.popen已知 Windows 中阻塞进程的问题,并且可能是这种情况。

我玩过其他库,包括 RubyOpen3.popen3Open4,但是遵循与上述非常相似的代码结构,该lame过程仍然挂起并且没有响应。

编辑#2

我发现这篇文章谈到了 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)
4

2 回答 2

3

你试过管道|字符吗?
使用 ruby​​ 安装程序在 Windows 上进行了测试

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 管道:如何将两个子进程的输出绑定在一起?

编辑: 使用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
于 2013-08-12T19:47:30.783 回答
0

使用pipeline来自Open3

require "open3"

wavCommand = "flac --decode --stdout \"#{file}\""
lameCommand = "lame -V0 --vbr-new - -"

Open3.pipeline(wavComamnd, lameCommand)

最后一行产生两个进程并将stdout第一个进程连接到stdin第二个进程。或者,您可以使用 访问stdin第一个进程,也可以使用pipeline_w获取stdout最后一个命令,pipeline_r或者您可以同时使用pipline_rw.

于 2019-04-20T22:19:16.420 回答