Redirecting standard output

Hi all,

Is there a way to redirect the standard output, so that command like
puts write to a file instead of the console?

Omar C. wrote:

Hi all,

Is there a way to redirect the standard output, so that command like
puts write to a file instead of the console?

Using this as a way to shamelessly promote my ‘desc_method’ gem… :slight_smile:

$stdout.methods
=> [:reopen, :print, …]

require ‘desc_method’
=> true

$stdout.desc_method :reopen
sig: #<Method: IO#reopen> arity: -1
appears to be a c method
searching ri for IO#reopen…
-------------------------------------------------------------- IO#reopen
ios.reopen(other_IO) => ios
ios.reopen(path, mode_str) => ios

 From Ruby 1.9.1

 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"

(end ri)
=> “#<Method: IO#reopen>”

$stdout.reopen(“abc”, “w”)

Enjoy
-r

Thanks, that’s exactly what I was looking for! Nice gem by the way. I
tried to “gem install” it, but I got a weird error message, don’t
remember what it was.

Omar C. wrote:

Thanks, that’s exactly what I was looking for! Nice gem by the way. I
tried to “gem install” it, but I got a weird error message, don’t
remember what it was.

No need for a gem.

$stdout = File.new ‘/path/to/file’, ‘w’
puts ‘foobar’
$stdout = STDOUT
puts ‘foobar’

Assignment to $stdout is deprecated (at least according to pickaxe),
using reopen is the right way to do it:

$stdout.reopen(File.new("/path/to/file", “w”))

Yup, you can use IO#reopen too. I don’t know about being “deprecated”.

$stdout.reopen will also redirect STDOUT to the location referenced in
your call to #reopen. How can you redirect $stdout to its original
location now?

The above solution ensures you can easily switch back.

  • Rob

On 11 déc. 2009, at 03:35, Robert G. wrote:

$stdout = File.new ‘/path/to/file’, ‘w’
puts ‘foobar’
$stdout = STDOUT
puts ‘foobar’

Assignment to $stdout is deprecated (at least according to pickaxe),
using reopen is the right way to do it:

$stdout.reopen(File.new("/path/to/file", “w”))

Yup, you can use IO#reopen too. I don’t know about being “deprecated”.

$stdout.reopen will also redirect STDOUT to the location referenced in
your call to #reopen. How can you redirect $stdout to its original
location now?

The above solution ensures you can easily switch back.

Answer to my own question:

FOO_BAR = STDOUT.dup
$stdout.reopen ‘/foo/bar/baz’, ‘w’
puts ‘foobar’
$stdout.reopen FOO_BAR

This method might be more useful if you want to redirect both $stdout,
and STDOUT.

2009/12/11 Robert G. [email protected]:

Omar C. wrote:

Thanks, that’s exactly what I was looking for! Nice gem by the way. I
tried to “gem install” it, but I got a weird error message, don’t
remember what it was.

No need for a gem.

The gem was not needed for the redirection functionality but for
getting at information about the method.

$stdout = File.new ‘/path/to/file’, ‘w’
puts ‘foobar’
$stdout = STDOUT
puts ‘foobar’

This does only work for redirections of output for the particular
process. It will fail in these cases

  • sub processes started
  • all entities which remembered $stdout instance (e.g. a logger)

Your last solution is better (although you do not necessarily need a
constant for that). Btw, apparently you don’t even need the mode -
that is inherited from the original. So you can do:

$ ruby19 -e ‘$stderr.reopen(“log”); $stderr.puts(“foo”)’ ; cat log
foo
$ ruby19 -e ‘$stdin.reopen(“log”); p gets’
“foo\n”
$

Cheers

robert

2009/12/11 Robert G. [email protected]:

This does only work for redirections of output for the particular
$ cat foobar.txt
Hello
=> 4301

Am I misunderstanding you?

I probably wasn’t specific enough: sub processes which use exec to
overlay the image will not inherit redirection if you simply assign to
$stdout:

16:24:59 ~$ ruby19 -e ‘$stdout = File.open(“log”,“w”); puts 123;
system(“date”)’
Fri Dec 11 16:25:05 WEST 2009
16:25:05 ~$ cat -n log
1 123
16:25:08 ~$ ruby19 -e ‘$stdout.reopen(“log”); puts 456; system(“date”)’
16:25:14 ~$ cat -n log
1 456
2 Fri Dec 11 16:25:13 WEST 2009
16:25:15 ~$

persist.
And exactly that is a problem: redirection does not take place for
whoever owns foo => it failed.

Cheers

robert

The gem was not needed for the redirection functionality but for
getting at information about the method.
Yup, I completely missed that! I was tired, and misread the thread
completely.

$stdout = File.new ‘/path/to/file’, ‘w’
puts ‘foobar’
$stdout = STDOUT
puts ‘foobar’

This does only work for redirections of output for the particular
process. It will fail in these cases

  • sub processes started

Hmm really? I thought a fork() would copy the memory from the parent
process into the subprocess.
Example:

$stdout = File.open ‘foobar.txt’, ‘w’; fork { $stdout.puts ‘Hello’ }

$ cat foobar.txt
Hello
=> 4301

Am I misunderstanding you?

  • all entities which remembered $stdout instance (e.g. a logger)

I’m not sure I follow you here either.
A reference to $stdout would persist.

foo = $stdout
$stdout = File.new ‘foobar.txt’, ‘w’

foo still references $stdout

Unless they use #dup, I don’t see how the reference to $stdout wouldn’t
persist.

Your last solution is better (although you do not necessarily need a
constant for that).

Yup. I just choose a constant to illustrate an example - if $stdout was
changing often, I’d use something more appropriate.

Btw, apparently you don’t even need the mode -
that is inherited from the original. So you can do:

$ ruby19 -e ‘$stderr.reopen(“log”); $stderr.puts(“foo”)’ ; cat log
foo
$ ruby19 -e ‘$stdin.reopen(“log”); p gets’
“foo\n”
$

Cool, good to know

I probably wasn’t specific enough: sub processes which use exec to
overlay the image will not inherit redirection if you simply assign to
$stdout:

16:24:59 ~$ ruby19 -e ‘$stdout = File.open(“log”,“w”); puts 123;
system(“date”)’
Fri Dec 11 16:25:05 WEST 2009
16:25:05 ~$ cat -n log
1 123
16:25:08 ~$ ruby19 -e ‘$stdout.reopen(“log”); puts 456; system(“date”)’
16:25:14 ~$ cat -n log
1 456
2 Fri Dec 11 16:25:13 WEST 2009
16:25:15 ~$

Ah! Okay. It makes sense now :slight_smile: Cool, I didn’t know that.

persist.
And exactly that is a problem: redirection does not take place for
whoever owns foo => it failed.

“whoever owns foo => it failed?” …? I don’t see any difference between
assigning $stdout and using $stdout.reopen in this case.

foo = $stdout = File.open ‘foo’, ‘w’
foo = $stdout.reopen ‘foo’, ‘w’

They’re both references to the same object. Can you elaborate or provide
an example? Thanks.

  • Rob

2009/12/11 Robert G. [email protected]:

persist.
And exactly that is a problem: redirection does not take place for
whoever owns foo => it failed.

“whoever owns foo => it failed?” …? I don’t see any difference between
assigning $stdout and using $stdout.reopen in this case.

foo = $stdout = File.open ‘foo’, ‘w’
foo = $stdout.reopen ‘foo’, ‘w’

That is not a proper model of the situation.

They’re both references to the same object. Can you elaborate or provide
an example? Thanks.

Now I’m tired and leave that as exercise for the reader.

Cheers

robert

Can anyone else provide an example?

This does only work for redirections of output for the particular
process. It will fail in these cases

  • sub processes started # thats cleared up.
  • all entities which remembered $stdout instance (e.g. a logger) # huh?

I don’t see how reassignment or #reopen would make a difference. They
reference the same object after all - for this particular case, their
behavior is the same.

So what gives?

  • Rob

On 12/11/2009 05:30 PM, Robert G. wrote:

So what gives?

I suggest you look again at my earlier posting (the one with the “date”
example) and / or look at the documentation. There is a difference
between reassigning and using #reopen - and it is significant.

Cheers

robert

I don’t know many of the technical aspects like subprocesses, but what I
did was this:

At the beginning of my program:
$stderr2 = $stderr.clone
$stderr.reopen(‘tmp2.dat’)

At the end:
$stderr.reopen($stderr2)
exit

Worked like a charm!

Robert K. wrote:

On 12/11/2009 05:30 PM, Robert G. wrote:

So what gives?

I suggest you look again at my earlier posting (the one with the “date”
example) and / or look at the documentation. There is a difference
between reassigning and using #reopen - and it is significant.

Cheers

robert

Yup, I’ve acknowledged the difference myself in all my previous posts.
I just couldn’t understand your second argument, but I think I know what
you mean from playing around in IRB.

Reassignment will create a new object… so

$stdout references a new object - foo references old.

foo = $stdout
$stdout = File.open ‘foo’, ‘w’

$stdout.reopen will keep the same object identity…

So all previous references will persist.

foo = $stdout
$stdout.reopen ‘foo’, ‘w’

Understood now. I can sleep.

  • Rob

Robert G. wrote:

I don’t see how reassignment or #reopen would make a difference. They
reference the same object after all - for this particular case, their
behavior is the same.

So what gives?

When you assign to $stdout, you are only changing the Ruby global
variable called ‘$stdout’

As it happens, Kernel#puts is just a shorthand for $stdout.puts, so it
will do what you expect. But the real stdout of the process (that is,
file descriptor 1 in the process’ FD table) is untouched. You can still
get at it using the STDOUT constant, or IO.new(1).

When you do $stdout.reopen(f), you are actually re-opening FD 1 with a
different file - underneath, ruby is doing a dup2() call to copy
f.fileno to FD 1. This means that the process’ actual stdout has
changed. This is what gets inherited by children you fork off.

So:

$ ruby -e ‘$stdout = File.open("/dev/null",“w”); system(“echo hello”)’
hello
$ ruby -e ‘$stdout.reopen(File.open("/dev/null",“w”)); system(“echo
hello”)’
$

As for deprecation: I have a suspicion it’s $defout not $stdout which is
deprecated.

Howdy,

Thanks for clarification. If you read the entire thread, you’d see:

I don’t see how reassignment or #reopen would make a difference. They
reference the same object after all - for this particular case, their
behavior is the same.

So what gives?

Refers to wondering how reassignment of $stdout & #reopen would make any
difference to previous references to $stdout. It’s cleared up now, as I
understand #reopen won’t change object identity but reassignment
obviously will.

Thanks for the rest of your post.

  • Rob