Temporary redirection of stdout

I’m new to ruby, so forgive any obvious stupididity, but can anyone
enlighten me as to why this…

#!/usr/bin/ruby -w

File.open(“output”,“w”) { |f|
puts(“To the screen”)
sso = $stdout
$stdout=f
puts(“To the file”)
$stdout = sso
puts(“To the screen again”)
}

does this…

root@bob rubyx # ./test
To the screen
root@bob rubyx # cat output
To the file
To the screen again

Thanks!

Andrew Walrond

File.open(“output”,“w”) { |f|
puts(“To the screen”)
sso = $stdout

This makes sso refer to the same object as $stdout does…

$stdout=f

… but since $stdout is a hooked variable, this doesn’t change the
object that $stdout refers to; it instead calls dup2() on the underlying
file descriptor…

puts(“To the file”)
$stdout = sso

… so that when you get here, this statement is no different from:

$stdout = $stdout

puts(“To the screen again”)
}

The solution is to change the line:

sso = $stdout

to:

sso = $stdout.dup

Paul

···

On Fri, May 02, 2003 at 11:15:03PM +0900, Andrew Walrond wrote:

I don’t see the same behavior:

vor-lord:/tmp> ruby -e 'File.open(“output”,“w”) { |f|
puts(“To the screen”)
sso = $stdout
$stdout=f
puts(“To the file”)
$stdout = sso
puts(“To the screen again”)
}

To the screen
To the screen again
vor-lord:/tmp> cat output
To the file
vor-lord:/tmp> ruby -v
ruby 1.7.2 (2002-02-21) [i386-linux]

This appears to be version specific:

vor-lord:/tmp> /usr/bin/ruby -e 'File.open(“output”,“w”) { |f|
puts(“To the screen”)
sso = $stdout
$stdout=f
puts(“To the file”)
$stdout = sso
puts(“To the screen again”)
}

To the screen
vor-lord:/tmp> cat output
To the file
To the screen again
vor-lord:/tmp> /usr/bin/ruby -v
ruby 1.6.7 (2002-03-19) [i386-linux]

···

On May 2, Andrew Walrond wrote:

I’m new to ruby, so forgive any obvious stupididity, but can anyone
enlighten me as to why this…

#!/usr/bin/ruby -w

File.open(“output”,“w”) { |f|
puts(“To the screen”)
sso = $stdout
$stdout=f
puts(“To the file”)
$stdout = sso
puts(“To the screen again”)
}

does this…

root@bob rubyx # ./test
To the screen
root@bob rubyx # cat output
To the file
To the screen again

… but since $stdout is a hooked variable…

“hooked variable”? I’ve never heard that nomenclature before; have I
skimmed over it in any of the common ruby documentation?

···

Do you Yahoo!?
The New Yahoo! Search - Faster. Easier. Bingo.

Or alternatively:

$defout = f
... to file
$defout = $stdout
... to screen

Or:

$defout = f
... to file
$defout = STDOUT
... to screen

I profess to not fully understanding the different between $defout, $stdout
and STDOUT; but I believe that $defout is where puts and friends send their
output by default, so you can change this to a different Ruby stream without
touching the stdout which is connected to the process.

Regards,

Brian.

···

On Fri, May 02, 2003 at 11:27:52PM +0900, Paul Brannan wrote:

The solution is to change the line:

sso = $stdout

to:

sso = $stdout.dup

Hi Paul

Paul Brannan wrote:

File.open(“output”,“w”) { |f|
puts(“To the screen”)
sso = $stdout

This makes sso refer to the same object as $stdout does…

Agreed; they are both references right?

$stdout=f

Which I’d expected to change the reference $stdout to point at the file
instead…

… but since $stdout is a hooked variable, this doesn’t change the
object that $stdout refers to; it instead calls dup2() on the underlying
file descriptor…

Hooked variable? So $stdout isn’t a reference after all?

puts(“To the file”)
$stdout = sso

… so that when you get here, this statement is no different from:

$stdout = $stdout

Agreed, if $stdout wasn’t actually a reference

The solution is to change the line:

sso = $stdout

to:

sso = $stdout.dup

Yep - already discovered that; just wanted to know why when “all
variables in Ruby are actually references”

Thanks for info

Andrew

···

On Fri, May 02, 2003 at 11:15:03PM +0900, Andrew Walrond wrote:

Hi Brett

I don’t see the same behavior:

vor-lord:/tmp> ruby -e 'File.open(“output”,“w”) { |f|
puts(“To the screen”)
sso = $stdout
$stdout=f
puts(“To the file”)
$stdout = sso
puts(“To the screen again”)
}

To the screen
To the screen again
vor-lord:/tmp> cat output
To the file
vor-lord:/tmp> ruby -v
ruby 1.7.2 (2002-02-21) [i386-linux]

This appears to be version specific:

vor-lord:/tmp> /usr/bin/ruby -e 'File.open(“output”,“w”) { |f|
puts(“To the screen”)
sso = $stdout
$stdout=f
puts(“To the file”)
$stdout = sso
puts(“To the screen again”)
}

To the screen
vor-lord:/tmp> cat output
To the file
To the screen again
vor-lord:/tmp> /usr/bin/ruby -v
ruby 1.6.7 (2002-03-19) [i386-linux]

This is interesting. Perhaps $stdout has been fixed to be a proper
reference in newer versions?

Whatever, the .dup solution should be fine for both versions, right?

Andrew

It’s described (briefly) in the Pickaxe,
http://www.rubycentral.com/book/ext_ruby.html

···

On Fri, May 02, 2003 at 11:44:11PM +0900, Michael Campbell wrote:

“hooked variable”? I’ve never heard that nomenclature before; have I
skimmed over it in any of the common ruby documentation?

http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=Pine.LNX.4.53.0303242333140.13691%40eli.fsl.noaa.gov

and view the complete thread.

sorry for the long link - i got frustrated by the ruby-talk search
capabilities and just used google.

-a

···

On Fri, 2 May 2003, Brian Candler wrote:

I profess to not fully understanding the different between $defout, $stdout
and STDOUT; but I believe that $defout is where puts and friends send their
output by default, so you can change this to a different Ruby stream without
touching the stdout which is connected to the process.

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ara.t.howard@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

Paul Brannan wrote:

This makes sso refer to the same object as $stdout does…

Agreed; they are both references right?

Yes.

… but since $stdout is a hooked variable, this doesn’t change the
object that $stdout refers to; it instead calls dup2() on the underlying
file descriptor…

Hooked variable? So $stdout isn’t a reference after all?

It is a reference, but you cannot change what object it is referring to.
When you think you are doing assignment, you are actually calling a C
function that calls dup2().

… so that when you get here, this statement is no different from:

$stdout = $stdout

Agreed, if $stdout wasn’t actually a reference

It is a reference, but a special kind of reference.

Paul

···

On Fri, May 02, 2003 at 11:57:55PM +0900, Andrew Walrond wrote:

Oh yes, I remember that now. But I don’t think it explains $stdout versus
STDOUT

Regards,

Brian.

···

On Sat, May 03, 2003 at 12:26:10AM +0900, ahoward wrote:

On Fri, 2 May 2003, Brian Candler wrote:

I profess to not fully understanding the different between $defout, $stdout
and STDOUT; but I believe that $defout is where puts and friends send their
output by default, so you can change this to a different Ruby stream without
touching the stdout which is connected to the process.

http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=Pine.LNX.4.53.0303242333140.13691%40eli.fsl.noaa.gov

and view the complete thread.

I profess to not fully understanding the different between $defout, $stdout
and STDOUT; but I believe that $defout is where puts and friends send their
output by default, so you can change this to a different Ruby stream without
touching the stdout which is connected to the process.

http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=Pine.LNX.4.53.0303242333140.13691%40eli.fsl.noaa.gov

and view the complete thread.

Oh yes, I remember that now. But I don’t think it explains $stdout versus
STDOUT

i am more confused than usual :wink:

~ > cat foo.rb
#!/usr/bin/env ruby

def dump s, binding=nil
case s
when Symbol
STDERR.puts %Q(\t#{s} => #{eval(s.to_s, binding).inspect})
when String
STDERR.puts “\t#{s}”
end
end

File.open(“output”,“w”) { |f|

dump ‘(ORIGINAL)’
[:f,:$defout,:$stdout,:STDOUT].map{|sym| dump sym, binding}

puts(“To the screen”)

$defout.reopen f
dump ‘(REDIRECTED TO FILE)’
[:f,:$defout,:$stdout,:STDOUT].map{|sym| dump sym, binding}

puts(“To the file”)

$defout.reopen STDOUT
dump ‘(RESTORED)’
[:f,:$defout,:$stdout,:STDOUT].map{|sym| dump sym, binding}

puts(“To the screen again”)
}

ruby foo.rb
(ORIGINAL)
f => #<File:0x40182b2c>
$defout => #IO:0x401880a4
$stdout => #IO:0x401880a4
STDOUT => #IO:0x401880a4
To the screen
(REDIRECTED TO FILE)
f => #<File:0x40182b2c>
$defout => #<File:0x401880a4>
$stdout => #<File:0x401880a4>
STDOUT => #<File:0x401880a4>
(RESTORED)
f => #<File:0x40182b2c>
$defout => #<File:0x401880a4>
$stdout => #<File:0x401880a4>
STDOUT => #<File:0x401880a4>

so it appears that reopening even $defout results in STDOUT and $stdout also
being reopened to point to a file. in otherwords, you will never be able to
restore unless a copy is made prior to this. this works like it reads:

cat redirect.rb && ruby redirect.rb && echo ‘output:’ && cat output
#!/usr/bin/env ruby

class IO
def redirect io
(@__self ||= ) << (clone = self.clone)
reopen io
clone
end
def restore
raise ‘NOT REDIRECTED’ unless
defined? @__self and not @__self.empty?
reopen @__self.pop
end
end

File.open(“output”,“w”) { |f|
puts(“To the screen”)

STDOUT.redirect f
puts(“To the file”)

STDOUT.restore
puts(“To the screen again”)
}

To the screen
To the screen again
output:
To the file

-a

···

On Sat, 3 May 2003, Brian Candler wrote:

On Sat, May 03, 2003 at 12:26:10AM +0900, ahoward wrote:

On Fri, 2 May 2003, Brian Candler wrote:

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ara.t.howard@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

IF you use reopen. That associates the same fd with a new file after
closing the old one, so the old stdout is lost for ever. Remember the Ruby
IO object is an abstraction on top of the stdio FILE on top of Unix fd…
File#reopen is essentially a ‘mutator’ on that object.

But you can do

$defout = File.open("myfile","w")

in which case you have not touched the $stdout/STDOUT object, you’ve just
reset $defout to point to a different one.

B.

···

On Sat, May 03, 2003 at 01:06:33AM +0900, ahoward wrote:

so it appears that reopening even $defout results in STDOUT and $stdout also
being reopened to point to a file. in otherwords, you will never be able to
restore unless a copy is made prior to this.

“ahoward” ahoward@fsl.noaa.gov schrieb im Newsbeitrag
news:Pine.LNX.4.53.0305021534540.25362@eli.fsl.noaa.gov

i am more confused than usual :wink:

I seems so… :slight_smile:

~ > cat foo.rb
#!/usr/bin/env ruby

def dump s, binding=nil
case s
when Symbol
STDERR.puts %Q(\t#{s} => #{eval(s.to_s, binding).inspect})
when String
STDERR.puts “\t#{s}”
end
end

File.open(“output”,“w”) { |f|

dump ‘(ORIGINAL)’
[:f,:$defout,:$stdout,:STDOUT].map{|sym| dump sym, binding}

puts(“To the screen”)

$defout.reopen f
dump ‘(REDIRECTED TO FILE)’
[:f,:$defout,:$stdout,:STDOUT].map{|sym| dump sym, binding}

puts(“To the file”)

$defout.reopen STDOUT
dump ‘(RESTORED)’
[:f,:$defout,:$stdout,:STDOUT].map{|sym| dump sym, binding}

puts(“To the screen again”)
}

ruby foo.rb
(ORIGINAL)
f => #<File:0x40182b2c>
$defout => #IO:0x401880a4
$stdout => #IO:0x401880a4
STDOUT => #IO:0x401880a4
To the screen
(REDIRECTED TO FILE)
f => #<File:0x40182b2c>
$defout => #<File:0x401880a4>
$stdout => #<File:0x401880a4>
STDOUT => #<File:0x401880a4>
(RESTORED)
f => #<File:0x40182b2c>
$defout => #<File:0x401880a4>
$stdout => #<File:0x401880a4>
STDOUT => #<File:0x401880a4>

so it appears that reopening even $defout results in STDOUT and $stdout
also
being reopened to point to a file. in otherwords, you will never be able
to
restore unless a copy is made prior to this. this works like it reads:

Err, why are you surprised? This is exactly what I would have expected
since $defout, $stdout and STDOUT initially point to the same instance. So
it doesn’t really matter on which of the instances you do issue ‘reopen’,
they “all” get redirected.

Redirecting should be done by assignment:

File.open(“output”,“w”) do |f|
old_defout = $defout

begin
puts “to the screen”

# redirect
$defout = f

puts "to the file"

ensure
$defout = old_defout
end
end

This is much simpler than your solution and works without adding code to IO
classes:

cat redirect.rb && ruby redirect.rb && echo ‘output:’ && cat output
#!/usr/bin/env ruby

class IO
def redirect io
(@__self ||= ) << (clone = self.clone)
reopen io
clone
end
def restore
raise ‘NOT REDIRECTED’ unless
defined? @__self and not @__self.empty?
reopen @__self.pop
end
end

File.open(“output”,“w”) { |f|
puts(“To the screen”)

STDOUT.redirect f
puts(“To the file”)

STDOUT.restore
puts(“To the screen again”)
}

To the screen
To the screen again
output:
To the file

Regards

robert

Err, why are you surprised? This is exactly what I would have expected
since $defout, $stdout and STDOUT initially point to the same instance. So
it doesn’t really matter on which of the instances you do issue ‘reopen’,
they “all” get redirected.

i’m suprised since STDOUT has the semantics of being a constant (i realize
that the object it points to is not constant). i would have thought that
changing $stdout (a variable) would not of changed STDOUT. all i’m saying
is of what value (in terms of POLS) is it to provied the constant STDOUT and
the variable $stdout, when in fact both are ‘variable’. i’m completely
understand what’s going on, but think it violated POLS for most people.

Redirecting should be done by assignment:

File.open(“output”,“w”) do |f|
old_defout = $defout

begin
puts “to the screen”

# redirect
$defout = f

puts "to the file"

ensure
$defout = old_defout
end
end

This is much simpler than your solution and works without adding code to IO
classes:

it’s simpler in this case, but i would maintain that adding to class IO is
good because

  • IO objects should (IMHO) have a redirect method - it is a common
    operation.

  • adding code to class IO is more generic that the above:

    #!/usr/bin/env ruby
    require ‘redirect.rb’

    bitbucket = open ‘/dev/null’, ‘w’
    $stderr.redirected(bitbucket) do
    system ‘ls non-existent-file’
    end

    it is often the case that > 1 fd needs redirected

  • easier to read (see above)

  • safer - ensure cannot be forgetton by programmer

  • class derived from IO will also have this behavior

  • it is more flexible:

    log = open ‘log’, ‘w’

    $stdout.redirected(log) do

    special_log = open ‘special’, ‘w’
    $stdout.redirected(special_log) do

    extra_special_log = open ‘special’, ‘w’
    $stdout.redirected(extra_special_log) do

    end
    end
    end

  • it is 10 lines of code!

anyhow - i can appreciate your opinion but i’m using it in my scripts :wink: but
then again i love the entire

Resource.aquire do |resource|

end

paradigm and the way it makes code read - to me it feels somehow more
ruby-ish.

-a

···

On Fri, 2 May 2003, Robert wrote:

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ara.t.howard@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

Err, why are you surprised? This is exactly what I would have expected
since $defout, $stdout and STDOUT initially point to the same instance. So
it doesn’t really matter on which of the instances you do issue ‘reopen’,
they “all” get redirected.

i’m suprised since STDOUT has the semantics of being a constant (i realize
that the object it points to is not constant). i would have thought that
changing $stdout (a variable) would not of changed STDOUT.

But you didn’t change the variable $stdout; you called $stdout.reopen (well,
$defout.reopen actually, but they were pointing at the same object)

all i’m saying
is of what value (in terms of POLS) is it to provied the constant STDOUT and
the variable $stdout, when in fact both are ‘variable’.

I don’t think it’s any different to:

I_AM_A_CONSTANT = []

I_AM_A_CONSTANT << "element"

p I_AM_A_CONSTANT   #>> ["element"]

The sense of “constant” is “this identifier cannot be changed to point to a
different object”, but that doesn’t stop you calling methods which change
its internal state.

If you change the state of the process’s stdout using $stdout.close or
$stdout.reopen, I don’t think you can’t get back what it was originally
connected to (and it doesn’t really make sense - e.g. if something had been
listening to your process’s stdout via a pipe, it would have seen the pipe
closed).

Regards,

Brian.

···

On Sat, May 03, 2003 at 04:07:42AM +0900, ahoward wrote:

On Fri, 2 May 2003, Robert wrote:

I’ve used the following in the past. Now that 1.8 has StringIO, its
even better …

def redirect_output(output)
old_output = $defout
$defout = output
yield
ensure
$defout = old_output
end

And then …

require ‘stringio’
sio = StringIO.new
redirect_output(sio) { puts “HI” }
p sio.string # => “HI\n”

By using $defout, I can redirect to string objects (great for
testing!). If I try to use reopen, then I can’t redirect to StringIO
objects :frowning:

···

On Fri, 2003-05-02 at 15:07, ahoward wrote:

anyhow - i can appreciate your opinion but i’m using it in my scripts :wink: but
then again i love the entire

Resource.aquire do |resource|

end

paradigm and the way it makes code read - to me it feels somehow more
ruby-ish.


– Jim Weirich jweirich@one.net http://w3.one.net/~jweirich

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

“ahoward” ahoward@fsl.noaa.gov schrieb im Newsbeitrag
news:Pine.LNX.4.53.0305021830390.7377@eli.fsl.noaa.gov

Err, why are you surprised? This is exactly what I would have
expected
since $defout, $stdout and STDOUT initially point to the same
instance. So
it doesn’t really matter on which of the instances you do issue
‘reopen’,
they “all” get redirected.

i’m suprised since STDOUT has the semantics of being a constant (i
realize
that the object it points to is not constant). i would have thought
that
changing $stdout (a variable) would not of changed STDOUT. all i’m
saying
is of what value (in terms of POLS) is it to provied the constant STDOUT
and
the variable $stdout, when in fact both are ‘variable’. i’m
completely
understand what’s going on, but think it violated POLS for most people.

The important thing to remember is that variables and constants are
references (like in Java for types deriving from Object). The constness of
a variable is restricted to the reference, i.e., a constant always points
to the same instance. You can of course freeze that instance in order to
achieve something similar to a real constant, but the semantics of freeze
are type dependent. And in the case of a stream freezing does not seem to
be of much use either…

it’s simpler in this case, but i would maintain that adding to class
IO is
good because

  • IO objects should (IMHO) have a redirect method - it is a common
    operation.

Maybe.

  • adding code to class IO is more generic that the above:

    #!/usr/bin/env ruby
    require ‘redirect.rb’

    bitbucket = open ‘/dev/null’, ‘w’
    $stderr.redirected(bitbucket) do
    system ‘ls non-existent-file’
    end

    it is often the case that > 1 fd needs redirected

That as easily done with begin - ensure.

  • easier to read (see above)

Granted.

  • safer - ensure cannot be forgetton by programmer

??? Sorry, I don’t understand this. Did you mean to say “can be
forgotten”?

  • class derived from IO will also have this behavior

Granted.

  • it is more flexible:

    log = open ‘log’, ‘w’

    $stdout.redirected(log) do

    special_log = open ‘special’, ‘w’
    $stdout.redirected(special_log) do

    extra_special_log = open ‘special’, ‘w’
    $stdout.redirected(extra_special_log) do

    end
    end
    end

You can do that as well with begin - ensure.

  • it is 10 lines of code!

Yes, but the crucial part was missing in your posting. (see below)

anyhow - i can appreciate your opinion but i’m using it in my scripts
:wink: but
then again i love the entire

Resource.aquire do |resource|

end

paradigm and the way it makes code read - to me it feels somehow more
ruby-ish.

But in that case your method definition should have looked differnt. At
least your redirect method does not test for a block, does not invoke the
block and does no cleanup if there is a block.

But that’s the nice thing about ruby, that there are often many good ways
to a solution and there’s something for everybody. :slight_smile:

Kind regards

robert
···

On Fri, 2 May 2003, Robert wrote:

nice point. i’m sold.

-a

···

On Sat, 3 May 2003, Jim Weirich wrote:

By using $defout, I can redirect to string objects (great for
testing!). If I try to use reopen, then I can’t redirect to StringIO
objects :frowning:

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ara.t.howard@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

“Brian Candler” B.Candler@pobox.com schrieb im Newsbeitrag
news:20030502215756.GA43281@uk.tiscali.com

Err, why are you surprised? This is exactly what I would have
expected
since $defout, $stdout and STDOUT initially point to the same
instance. So
it doesn’t really matter on which of the instances you do issue
‘reopen’,
they “all” get redirected.

i’m suprised since STDOUT has the semantics of being a constant (i
realize
that the object it points to is not constant). i would have thought
that
changing $stdout (a variable) would not of changed STDOUT.

But you didn’t change the variable $stdout; you called $stdout.reopen
(well,
$defout.reopen actually, but they were pointing at the same object)

all i’m saying
is of what value (in terms of POLS) is it to provied the constant
STDOUT and
the variable $stdout, when in fact both are ‘variable’.

I don’t think it’s any different to:

I_AM_A_CONSTANT = []

I_AM_A_CONSTANT << "element"

p I_AM_A_CONSTANT   #>> ["element"]

The sense of “constant” is “this identifier cannot be changed to point
to a
different object”, but that doesn’t stop you calling methods which
change
its internal state.

Even worse, you can change the value but get a warning. Or is there any
command line option that prevents value changes on constants?

If you change the state of the process’s stdout using $stdout.close or
$stdout.reopen, I don’t think you can’t get back what it was originally
connected to (and it doesn’t really make sense - e.g. if something had
been
listening to your process’s stdout via a pipe, it would have seen the
pipe
closed).

Exactly!

robert
···

On Sat, May 03, 2003 at 04:07:42AM +0900, ahoward wrote:

On Fri, 2 May 2003, Robert wrote: