Forum: Ruby assignment to $stdout deprecated?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-02-08 23:30
In pickaxe2, on p. 335, it says that assigning to $stdout is deprecated
and to use $stdout.reopen() instead:

-------------------------------------------------------------- IO#reopen
     ios.reopen(other_IO)         => ios
     ios.reopen(path, mode_str)   => ios
------------------------------------------------------------------------
     Reassociates _ios_ with the I/O stream given in _other_IO_ or to a
     new stream opened on _path_. This may dynamically change the actual
     class of this stream.

        f1 = File.new("testfile")
        f2 = File.new("testfile")
        f2.readlines[0]   #=> "This is line one\n"
        f2.reopen(f1)     #=> #<File:testfile>
        f2.readlines[0]   #=> "This is line one\n"


And there is this in the Standard LIbrary:

StringIO

Once a string is wrapped in a StringIO object, it can be read from and
written to as if it were an open file....It also lets you pass strings
into classes and methods that were originally written to work with
files.

But I get an error trying to replace $stdout with a StringIO object:

require "stringio"

strio = StringIO.new
old_out = $stdout
$stdout.reopen(strio)

--output:--
r1test.rb:5:in `reopen': cannot convert StringIO into String (TypeError)
        from r1test.rb:5

What am I doing wrong?
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-02-08 23:38
I should add that assigning a StringIO object to $stdout does work:

require "stringio"

strio = StringIO.new
strio.write("hello world")

old_out = $stdout
$stdout = strio
puts " goodbye"
$stdout = old_out

strio.rewind()
puts strio.read()

--output:--
hello world goodbye
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-02-08 23:41
7stud -- wrote:
> -------------------------------------------------------------- IO#reopen
>         f1 = File.new("testfile")
>         f2 = File.new("testfile")
>         f2.readlines[0]   #=> "This is line one\n"
>         f2.reopen(f1)     #=> #<File:testfile>
>         f2.readlines[0]   #=> "This is line one\n"
>
>

Also, what in the world is that example supposed to demonstrate?
F889bf17449ffbf62345d2b2d316a937?d=identicon&s=25 Michal Suchanek (Guest)
on 2009-02-09 11:54
(Received via mailing list)
2009/2/8 7stud -- <bbxx789_05ss@yahoo.com>:
>
>
> old_out = $stdout
> $stdout.reopen(strio)
>
> --output:--
> r1test.rb:5:in `reopen': cannot convert StringIO into String (TypeError)
>        from r1test.rb:5
>
> What am I doing wrong?

Not reading the docs.

It should be obvious from description of IO#reopen that you cannot use
StringIO in this way because it does not have an underlying file
descriptor.

On the other hand, you can assign a StrinIO into $stdout but this will
change only the global, not the file descriptors seen by C extensions
or programs executed with system().

HTH

Michal
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-02-09 11:59
Michal Suchanek wrote:
>> What am I doing wrong?
>
> Not reading the docs.
>
> It should be obvious from description of IO#reopen that you cannot use
> StringIO in this way because it does not have an underlying file
> descriptor.
>

I posted the docs.  They say no such thing.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-02-09 12:17
7stud -- wrote:
> In pickaxe2, on p. 335, it says that assigning to $stdout is deprecated
> and to use $stdout.reopen() instead:

It depends what you're trying to do. I'll describe this from a Unix
point of view.

Your Ruby program runs as a process which starts with three open file
descriptors: stdin (fd 0), stdout (fd 1), stderr (fd 2). These are
wrapped in Ruby objects STDIN, STDOUT, STDERR, and the global variables
$stdin, $stdout, $stderr point to these objects too.

If you do STDOUT.reopen(...) then you are closing fd 1 and replacing it
with a different file in the Unix file descriptor table. When your Ruby
program writes to STDOUT it will write to this file; but also any child
process spawned by your Ruby program will inherit this too (e.g. using
system() or backticks)

If you reassign $stdout to point to a completely different Ruby object,
then any code you write which does $stdout.puts will write to this
object - but FD 1 still remains connected to the original stdout.
Therefore, STDOUT.puts will still write to the original destination, as
will any child process.

  $stdout = File.open("/tmp/stdout.txt","w")

  puts "Hello"            # uses $stdout, goes to the file
  STDOUT.puts "World"     # goes to the terminal (FD 1)
  system("echo Wheee")    # goes to the terminal (FD 1)

If you need to redirect to a StringIO object, then you have little
choice but to use $stdout, because a StringIO is not a Unix file, i.e.
it doesn't have an entry in the file descriptor table.

(If you wanted to get very fancy, you could perhaps set up a pipe,
connect stdout to the writer end, and have a Ruby thread reading from
the reader end and appending to a StringIO object. But you'd only jump
through those hoops if you wanted the stdout from spawned child
processes to write to the StringIO too, and in that case you'd be better
off using IO.popen anyway)
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-02-09 13:27
Brian Candler wrote:
>

Thanks for the detailed response.

> If you reassign $stdout to point to a completely different Ruby object,
> then any code you write which does $stdout.puts will write to this
> object - but FD 1 still remains connected to the original stdout.
>

Ok.

> Therefore, STDOUT.puts will still write to the original destination, as
> will any child process.
>

Ok.

>   $stdout = File.open("/tmp/stdout.txt","w")
>
>   puts "Hello"            # uses $stdout, goes to the file
>   STDOUT.puts "World"     # goes to the terminal (FD 1)
>   system("echo Wheee")    # goes to the terminal (FD 1)
>

AFAIK, all child processes receive a copy of their environment including
copies of all the variables in the parent.  So echo doesn't write to
$stdout because echo doesn't understand ruby and therefore ignores
variables like $stdout?


> If you need to redirect to a StringIO object, then you have little
> choice but to use $stdout, because a StringIO is not a Unix file, i.e.
> it doesn't have an entry in the file descriptor table.
>

Ok.

> (If you wanted to get very fancy, you could perhaps set up a pipe,
> connect stdout to the writer end, and have a Ruby thread reading from
> the reader end and appending to a StringIO object. But you'd only jump
> through those hoops if you wanted the stdout from spawned child
> processes to write to the StringIO too, and in that case you'd be better
> off using IO.popen anyway)

Thanks for taking the time to write such an informative post.
F889bf17449ffbf62345d2b2d316a937?d=identicon&s=25 Michal Suchanek (Guest)
on 2009-02-09 13:33
(Received via mailing list)
2009/2/9 7stud -- <bbxx789_05ss@yahoo.com>:
> I posted the docs.  They say no such thing.
Yes, only IO::for_fd mentions the use of fds, IO#reopen does not
mention this limitation. This is probably a bug in the documentation.

Thanks

Michal
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-02-09 14:03
7stud -- wrote:
> AFAIK, all child processes receive a copy of their environment including
> copies of all the variables in the parent.

Starting a different child process, such as a shell, involves fork()
followed by exec() in the child.

Immediately after the fork(), the child will have a copy of everything
the parent has, including the file descriptor table.

It then has the opportunity (if it wishes) to clean up the FD table
before calling exec(), and/or to pass a different environment or
arguments to the program which it is exec()ing.

> So echo doesn't write to
> $stdout because echo doesn't understand ruby and therefore ignores
> variables like $stdout?

Basically, yes. echo just writes to FD 1. It doesn't know anything about
Ruby or Ruby objects. Indeed since Ruby is running in a different
process, it cannot possibly access them (other than via some sort of
IPC, e.g. communicating over a pipe)

Regards,

Brian.
This topic is locked and can not be replied to.