# Assignment to $stdout deprecated?

**URL:** <https://www.ruby-forum.com/t/assignment-to-stdout-deprecated/159799>\
**Category:** Ruby\
**Created:** [February 8, 2009, 11:30pm UTC](https://www.ruby-forum.com/t/assignment-to-stdout-deprecated/159799 "2009-02-08T23:30:37Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![7stud](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/7/57b2e6/32.png) [@7stud](https://www.ruby-forum.com/u/7stud)\
**Post date:** [February 8, 2009, 11:30pm UTC](https://www.ruby-forum.com/t/assignment-to-stdout-deprecated/159799/1 "2009-02-08T23:30:37Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![7stud](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/7/57b2e6/32.png) [@7stud](https://www.ruby-forum.com/u/7stud)\
**Post date:** [February 8, 2009, 11:38pm UTC](https://www.ruby-forum.com/t/assignment-to-stdout-deprecated/159799/2 "2009-02-08T23:38:36Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![7stud](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/7/57b2e6/32.png) [@7stud](https://www.ruby-forum.com/u/7stud)\
**Post date:** [February 8, 2009, 11:41pm UTC](https://www.ruby-forum.com/t/assignment-to-stdout-deprecated/159799/3 "2009-02-08T23:41:52Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![7stud](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/7/57b2e6/32.png) [@7stud](https://www.ruby-forum.com/u/7stud)\
**Post date:** [February 9, 2009, 11:54am UTC](https://www.ruby-forum.com/t/assignment-to-stdout-deprecated/159799/4 "2009-02-09T11:54:30Z")

</div>

2009/2/8 7stud – [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> 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

---

<div class="post-metadata">

**Author:** ![7stud](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/7/57b2e6/32.png) [@7stud](https://www.ruby-forum.com/u/7stud)\
**Post date:** [February 9, 2009, 11:59am UTC](https://www.ruby-forum.com/t/assignment-to-stdout-deprecated/159799/5 "2009-02-09T11:59:24Z")

</div>

Michal S. 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.

---

<div class="post-metadata">

**Author:** ![7stud](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/7/57b2e6/32.png) [@7stud](https://www.ruby-forum.com/u/7stud)\
**Post date:** [February 9, 2009, 12:17pm UTC](https://www.ruby-forum.com/t/assignment-to-stdout-deprecated/159799/6 "2009-02-09T12:17:45Z")

</div>

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)

---

<div class="post-metadata">

**Author:** ![7stud](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/7/57b2e6/32.png) [@7stud](https://www.ruby-forum.com/u/7stud)\
**Post date:** [February 9, 2009, 1:27pm UTC](https://www.ruby-forum.com/t/assignment-to-stdout-deprecated/159799/7 "2009-02-09T13:27:30Z")

</div>

Brian C. 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.

---

<div class="post-metadata">

**Author:** ![7stud](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/7/57b2e6/32.png) [@7stud](https://www.ruby-forum.com/u/7stud)\
**Post date:** [February 9, 2009, 1:33pm UTC](https://www.ruby-forum.com/t/assignment-to-stdout-deprecated/159799/8 "2009-02-09T13:33:10Z")

</div>

2009/2/9 7stud – [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> 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

---

<div class="post-metadata">

**Author:** ![7stud](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/7/57b2e6/32.png) [@7stud](https://www.ruby-forum.com/u/7stud)\
**Post date:** [February 9, 2009, 2:03pm UTC](https://www.ruby-forum.com/t/assignment-to-stdout-deprecated/159799/9 "2009-02-09T14:03:43Z")

</div>

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.
