48

How do I redirect stderr and stdout to file for a Ruby script?

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
AOO
  • 481
  • 1
  • 4
  • 3

5 Answers5

70

From within a Ruby script, you can redirect stdout and stderr with the IO#reopen method.

# a.rb
$stdout.reopen("out.txt", "w")
$stderr.reopen("err.txt", "w")

puts 'normal output'
warn 'something to stderr'
$ ls
a.rb
$ ruby a.rb
$ ls
a.rb    err.txt out.txt
$ cat err.txt 
something to stderr
$ cat out.txt 
normal output
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
Mark Rushakoff
  • 249,864
  • 45
  • 407
  • 398
  • 2
    How can normal $stdout be restored again afterwards? – shevy Feb 03 '15 at 20:17
  • why should we use reopen method ? it's mean that if "out.txt" is not exists, it will be created? – wind May 17 '16 at 03:43
  • Take a look at [this answer](http://stackoverflow.com/a/22777806/1287376) on how to restore $stdout afterwards. – Matheus Santana Jul 21 '16 at 16:07
  • 7
    You'll need to `$stdout.sync = true` and `$stderr.sync = true` if you hope to, say, tail the file while lines are being written. Otherwise, the lines won't be written out until the process closes, or something else like that flushes the streams. – Pistos Mar 26 '17 at 00:44
13

Note: reopening of the standard streams to /dev/null is a good old method of helping a process to become a daemon. For example:

# daemon.rb
$stdout.reopen("/dev/null", "w")
$stderr.reopen("/dev/null", "w")
argent_smith
  • 476
  • 6
  • 11
  • 3
    What do you mean by "helping a process become a daemon"? – silvamerica Apr 25 '11 at 18:47
  • 4
    In UNIX in order to make a program a daemon the programmer should not merely fork it into background but also redirect it's standard IO streams. Are you familiar with this? – argent_smith Apr 28 '11 at 17:20
  • Note: If using built-in `Process.daemon` it can do this for you. See [docs here](http://ruby-doc.org/core-2.2.2/Process.html#method-c-daemon). – captainpete Nov 19 '15 at 03:12
10
def silence_stdout
  $stdout = File.new( '/dev/null', 'w' )
  yield
ensure
  $stdout = STDOUT
end
grosser
  • 14,707
  • 7
  • 57
  • 61
4
./yourscript.rb 2>&1 > log.txt

will redirect stdout and stderr to the same file.

Richard Fearn
  • 25,073
  • 7
  • 56
  • 55
  • 4
    Assuming you are running on *nix – Steve Weet Jun 11 '10 at 06:50
  • @Steve: I think `>` works on Windows - I'm not sure about `2>` though. – Andrew Grimm Apr 07 '11 at 02:04
  • 2
    This answer also makes assumptions about what shell the user is using, even within *nix. Some shells have different redirection styles, for instance the default behavior of zsh. – Daisy Sophia Hollman Jan 05 '12 at 16:10
  • It does not. Redirection order matters. It first redirects stderr to stdout, making stderr a COPY of the initial stdout, then redirect stdout to the file. Only stdout is redirected to a file. to redirect both stdout and stderr to the same file, use `./yourscript.rb > log.txt 2>&1`. As of 2022, some shells support `&>` or `>&` to redirect both descriptors at the same time (bash 5.2 or zsh 5.8 for example) – exore Oct 11 '22 at 01:15
3

A full example with $stdout and $stderr redirected to a file and how to restore the initial behavior.

#!/usr/bin/ruby

logfile = "/tmp/testruby.log"

@original_stdout = $stderr.dup
@original_stderr = $stderr.dup
$stdout.reopen(logfile, "w")
$stdout.sync = true
$stderr.reopen($stdout)


def restore_stdout
  $stdout.reopen(@original_stdout)
  $stderr.reopen(@original_stderr)
end

def fail_exit(msg)
  puts "- #{msg}" # to the logfile
  restore_stdout
  $stderr.puts "+ #{msg}" # to standard error
  exit!
end

def success_exit(msg)
  puts "- #{msg}" # to the logfile
  restore_stdout  
  $stdout.puts "+ #{msg}" # to standard output
  exit
end

puts "This message goes to the file"

success_exit "A successful exit message"
Atika
  • 1,560
  • 18
  • 18