在Ruby中,我分配给$stdout的对象会发生什么变化?

在Ruby中,我分配给$stdout的对象会发生什么变化?,ruby,Ruby,编辑:不要费心看这个问题,我就是不能删除它。它基于破损的代码,这里(几乎)没有什么可学的。 我在Ruby程序中重定向控制台输出,尽管它工作得很好,但有一件事我很好奇: 这是我的密码 capture = StringIO.new $stdout = capture puts "Hello World" 看起来,即使我正在将我的捕获对象分配给$stdout,$stdout在分配后包含一个新的不同对象,但至少类型是正确的 换言之: $stdout.to_s # =>

编辑:不要费心看这个问题,我就是不能删除它。它基于破损的代码,这里(几乎)没有什么可学的。

我在Ruby程序中重定向控制台输出,尽管它工作得很好,但有一件事我很好奇:

这是我的密码

capture = StringIO.new
$stdout = capture
puts "Hello World"
看起来,即使我正在将我的
捕获
对象分配给
$stdout
$stdout
在分配后包含一个新的不同对象,但至少类型是正确的

换言之:

$stdout.to_s              # => #<IO:0x2584b30>

capture = StringIO.new
$stdout = capture

$stdout.to_s              # => #<StringIO:0x4fda948>
capture.to_s              # => #<StringIO:0x4e3b220>
$stdout.to#s=>#
capture=StringIO.new
$stdout=捕获
$stdout.to#s=>#
capture.to_#s=>#
随后,
$stdout.string
包含
“Hello World”
,但
捕获.string
为空

是幕后发生了什么事还是我在这里遗漏了什么

编辑:这可能仅限于某些版本。我在Windows 8.1上使用Ruby 2.0.0-p247,它可以正常工作

>> capture = StringIO.new
=> #<StringIO:0x00000001ea8c00>
>> $stdout = capture
>> $stdout.to_s
>> capture.to_s 
$stdout.to_s
capture.to_s
给我同样的结果

我使用了ruby 1.9.3。(与2.0.0相同)

它按预期工作

>> capture = StringIO.new
=> #<StringIO:0x00000001ea8c00>
>> $stdout = capture
>> $stdout.to_s
>> capture.to_s 
$stdout.to_s
capture.to_s
给我同样的结果


我使用了ruby 1.9.3。(与2.0.0相同)

您确定在这两者之间没有发生其他对
$stdout
捕获的操作吗

对我来说,输出看起来不同。
capture
$stdout
都是同一个对象,随后以相同的响应回答
string
(ruby 1.9.2):

需要“stringio”
$stdout.to#u s#=>#
capture=StringIO.new
$stdout=捕获
将$stdout.to_#s=>#
将capture.to_s#=>#
将“重定向”
$stderr.put$stdout.string#=>“#\n#\n定向”
$stderr.puts capture.string#=>“#\n#\n定向”

您确定在这两者之间没有其他对
$stdout
捕获的操作发生吗

对我来说,输出看起来不同。
capture
$stdout
都是同一个对象,随后以相同的响应回答
string
(ruby 1.9.2):

需要“stringio”
$stdout.to#u s#=>#
capture=StringIO.new
$stdout=捕获
将$stdout.to_#s=>#
将capture.to_s#=>#
将“重定向”
$stderr.put$stdout.string#=>“#\n#\n定向”
$stderr.puts capture.string#=>“#\n#\n定向”

尽管这个问题是由于忽略了对
$stdout
值的更改,但Ruby确实能够以这种方式重写对全局变量的赋值,至少在C api中是这样,使用

检查新值是否合适(它会检查),如果不合适,则引发异常

如果您确实需要(您不需要),您可以创建一个扩展,该扩展定义一个全局变量,该变量自动存储一个不同于指定值的对象,可以在该对象上调用
dup
,并使用该变量:

#包括“ruby.h”
价值观;
静态void foo_setter(值val、ID ID、值*var){
值dup_val=rb_funcall(val,rb_intern(“dup”),0);
*var=dup_val;
}
void Init_hooked(){
rb_define_hooked_变量($foo“,&foo,0,foo_setter);
}
然后你可以像这样使用它:

2.0.0-p247:001>要求“/ext/hooked”
=>正确
2.0.0-p247:002>s=Object.new
=> # 
2.0.0-p247:003>$foo=s
=> # 
2.0.0-p247:004>s.to\U.s
=> "#" 
2.0.0-p247:005>$foo.to\u
=> "#" 
2.0.0-p247:006>s==$foo
=>错误
当然,这非常类似于在类中创建setter方法,该类
dup
s vale并存储该值,您可以在普通Ruby中执行此操作:

def foo=(新的_foo)
@foo=new_foo.dup
终止

由于使用全局变量通常是一种糟糕的设计,因此在Ruby中不可能使用全局变量似乎是合理的。

虽然这个问题是由于忽略了对
$stdout
值的更改,但Ruby确实能够通过这种方式重写对全局变量的赋值,至少在C api中是这样

检查新值是否合适(它会检查),如果不合适,则引发异常

如果确实需要(不需要),可以创建一个扩展,该扩展定义一个全局变量,该变量自动存储不同于值的对象
require 'stringio'                                                                                                                             
$stdout.to_s              # => #<IO:0x2584b30>                                                                                                 

capture = StringIO.new                                                                                                                         
$stdout = capture                                                                                                                              

puts $stdout.to_s              # => #<StringIO:0x89a38c0>                                                                                      
puts capture.to_s              # => #<StringIO:0x89a38c0>                                                                                      
puts "redirected"

$stderr.puts $stdout.string # => '#<StringIO:0x89a38c0>\n#<StringIO:0x89a38c0>\nredirected'                                                                           
$stderr.puts capture.string # => '#<StringIO:0x89a38c0>\n#<StringIO:0x89a38c0>\nredirected'