我需要使用Marshal在Ruby中序列化一个对象,并通过管道将其发送到子进程。我怎么能这样做?
我的代码如下所示,我的问题在评论中:
data = Marshal.dump(data)
#call sub-process
`ruby -r a_lib -e 'a_method'` #### how to send the stdout to the subprocess?
a_method看起来像:
def a_method
...
data = Marshal.load(data) #### how to load the stdout of the parent process?
...
end
答案 0 :(得分:2)
是的,你可以在不同的ruby / non-ruby进程之间通过管道发送序列化对象!
让我告诉你我是怎么做的。
在此示例中,主进程启动子进程,然后子进程使用Marshal序列化传输简单的Hash对象。
首先在Process类中声明一些辅助方法 run_ruby 会很有用:
#encoding: UTF-8
require 'rbconfig'
module Process
RUBY = RbConfig::CONFIG.values_at('bindir', 'BASERUBY').join('/')
# @param [String] command
# @param [Hash] options
def Process.run_ruby(command, options)
spawn("#{Process::RUBY} -- #{command}", options)
end
end
此代码只是找到ruby可执行文件并将完整路径保存到RUBY常量。
重要:如果您要使用 Jruby 或其他一些可执行文件 - 你应该重写这段代码并提供执行它的路径!
接下来,我们应该开始子进程
目前,我们可以为新流程覆盖 STDIN , STDOUT 和 STDERR 。
让我们创建一个管道并将子项的 STDOUT 重定向到此管道:
rd, wr = IO.pipe
Process.run_ruby("./test/pipetest.rb param1 param2", {:out => wr})
wr.close
请注意选项哈希: {:out => wr} - 它告诉spawn命令将 STDOUT 重定向到 wr 流描述符。
此外,您可以在命令行中指定params(请参阅 param1 和 param2 )。
请注意,我们称之为 wr.close ,因为在此示例中我们不在父进程中使用它。
主人如何接收对象:
message = rd.gets # read message header with size in bytes
cb = message[5..-1].to_i # message is in form: "data <byte_size>\n"
data = rd.read(cb) # read message with binary object
puts "Parent read #{data.length} from #{cb} bytes:"
obj = Marshal::load(data) # unserialize object
puts obj.inspect
现在,如何传输序列化对象?
首先,孩子会序列化对象,
然后它将以以下形式发送父消息:"data <byte_size>\n"
之后它将自己发送序列化对象
子进程会将对象发送到 STDOUT ,因为我们已指定将此通道用作管道。
#encoding: UTF-8
# obj is an example Hash object to be transmitted
obj = {
1 => 'asd',
'data' => 255,
0 => 0.55
}
data = Marshal::dump(obj) # serializing object (obj)
$stdout.puts "data #{data.length}" # sending message header
$stdout.write data # sending message itself
$stdout.flush # Important: flush data!
在上面的代码中,子进程只输出一个序列化对象并终止
但是,当然,你可以编写更复杂的行为
例如,我启动了许多子流程,每个流程在 STDOUT 中共享同一个管道到父流程。为了避免两个孩子同时写入管道的问题,我必须使用系统级Mutex (而不是Ruby Mutex)来控制对此管道的访问。
答案 1 :(得分:0)
您可以使用IO::pipe
方法。
我认为您选择的不是创建子进程的最佳方式。反引号在场景后面fork
和exec
,ruby
命令也在fork
和exec
。这意味着您的命令:
`ruby -r a_lib -e 'a_method'`
执行以下操作:fork current进程,将其转换为shell进程,fork shell进程,将其转换为ruby进程。
我建议使用fork
方法:
data = Marshal.dump(data)
reader, writer = IO.pipe
reader.close # parent process will be on the writing side of the pipe
writer.puts data
#call sub-process
fork do
writer.close # child process can only read from the pipe
data = reader.gets
# whatever needs to be done with data
end