Redirecting standard output

Omar Campos 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
--
Posted via http://www.ruby-forum.com/\.

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.

路路路

--
Posted via http://www.ruby-forum.com/.

Omar Campos 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'

路路路

--
Posted via http://www.ruby-forum.com/\.

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"))

路路路

On 11 d茅c. 2009, at 03:35, Robert Gleeson wrote:

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

--
Luc Heinrich - luc@honk-honk.com

Omar Campos 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 Gleeson <rob@flowof.info>:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

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

路路路

--
Posted via http://www.ruby-forum.com/\.

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

路路路

--
Posted via http://www.ruby-forum.com/\.

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.

路路路

--
Posted via http://www.ruby-forum.com/\.

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?

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 ~$

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

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

Cheers

robert

路路路

2009/12/11 Robert Gleeson <rob@flowof.info>:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

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

路路路

--
Posted via http://www.ruby-forum.com/\.

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

路路路

2009/12/11 Robert Gleeson <rob@flowof.info>:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

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

路路路

--
Posted via http://www.ruby-forum.com/\.

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

路路路

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

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?

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert Gleeson 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.

路路路

--
Posted via http://www.ruby-forum.com/\.

Robert Klemme 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

路路路

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

--
Posted via http://www.ruby-forum.com/\.

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

路路路

--
Posted via http://www.ruby-forum.com/\.

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!

路路路

--
Posted via http://www.ruby-forum.com/.