如何在不停止使用Ruby显示屏幕的情况下将STDOUT复制到文件中

时间:2012-02-24 16:02:26

标签: ruby

我正在尝试将stdout复制到文件以进行日志记录。我还希望它显示在我正在使用的IDE的Ruby控制台中。

我将此代码插入到我的脚本中,并将$stdout重定向到my.log文件:

$stdout.reopen("my.log", "w")

有没有人知道将$stdout的内容复制到文件而不是将其重定向到文件的gem或技术?另外,我没有使用Rails只是Ruby。

4 个答案:

答案 0 :(得分:9)

这样的事可能会对你有所帮助:

class TeeIO < IO

  def initialize orig, file
    @orig = orig
    @file = file
  end

  def write string
    @file.write string
    @orig.write string
  end

end

IO中输出的大多数方法最终都使用write,因此您只需要覆盖这一方法。您可以像这样使用它:

#setup
tee = TeeIO.new $stdout, File.new('out.txt', 'w')
$stdout = tee

# Now lots of example uses:
puts "Hello"
$stdout.puts "Extending IO allows us to expicitly use $stdout"
print "heres", :an, :example, "using", 'print', "\n"
48.upto(57) do |i|
  putc i
end
putc 10 #newline
printf "%s works as well - %d\n", "printf", 42
$stdout.write "Goodbye\n"

在此示例之后,以下内容与两者控制台和文件相同:

Hello
Extending IO allows us to expicitly use $stdout
heresanexampleusingprint
0123456789
printf works as well - 42
Goodbye

我不会声称这种技术是失败证明,但它应该适用于stdout的简单使用。测试它以供您使用。

请注意,您不必在reopen上使用$stdout,除非您想要重定向子进程或不合作扩展的输出。简单地为其分配不同的IO对象将适用于大多数用途。


RSpec的

RSpec命令行在之前引用$stdout ,您可以运行任何代码来重新分配它,因此这不起作用。 reopen在这种情况下仍然可以正常工作,因为您正在更改$stdout指向的实际对象和RSpec所具有的引用,但这并不会为您提供输出。

一种解决方案是像这样修补$stdout

$out_file = File.new('out.txt', 'w')

def $stdout.write string
  $out_file.write string
  super
end

这有效,但与所有猴子修补一样,要小心。使用操作系统的tee命令会更安全。

答案 1 :(得分:4)

如果您使用的是Linux或Mac OS,操作系统中提供的tee命令可以轻松完成此操作。从其手册页:

NAME
     tee -- pipe fitting

SYNOPSIS
     tee [-ai] [file ...]

DESCRIPTION
     The tee utility copies standard input to standard output, making a copy in zero or more files.  The output is unbuffered.

类似于:

echo '>foo bar' | tee tmp.out
>foo bar

将输出回显到STDOUT和文件。捕获文件给了我:

cat tmp.out
>foo bar

否则,如果您想在代码中执行此操作,则这是一项简单的任务:

def tee_output(logfile)
  log_output = File.open(logfile, 'w+')
  ->(o) {
    log_output.puts o
    puts o
  }
end

tee = tee_output('tmp.out')
tee.call('foo bar')

运行它:

>ruby test.rb 
foo bar

检查输出文件:

>cat tmp.out
foo bar

我使用"w+"将我的文件访问权限附加到输出文件,而不是覆盖它。

CAVEAT :这会打开文件,并在您调用tee_output方法后的代码生命周期中将其保持打开状态。这让一些人感到困扰,但是,就个人而言,它并没有让我感到烦恼,因为Ruby会在脚本退出时关闭文件。一般情况下,我们希望在完成文件后立即关闭文件,但在您的代码中,打开它并保持打开更有意义,而不是重复打开和关闭输出文件,但您的里程可能会有所不同。


编辑:

对于Ruby 1.8.7,使用lambda而不是新的->语法:

def tee_output(logfile)
  log_output = File.open(logfile, 'w+')
  lambda { |o|
    log_output.puts o
    puts o
  }
end

tee = tee_output('tmp.out')
tee.call('foo bar')

答案 2 :(得分:1)

我知道这是一个老问题,但我发现自己处于同样的境地。我编写了一个扩展File并覆盖write putsclose方法的Multi-IO类,我还确保其线程安全:

require 'singleton'

class MultiIO < File
  include Singleton 
  @@targets = []
  @@mutex = Mutex.new

  def self.instance
    self.open('/dev/null','w+')
  end

  def puts(str)
    write "#{str}\n"
  end

  def write(str)
    @@mutex.synchronize do 
      @@targets.each { |t| t.write str; flush }
    end
  end

  def setTargets(targets)
    raise 'setTargets is a one-off operation' unless @@targets.length < 1
    targets.each do |t|
       @@targets.push STDOUT.clone if t == STDOUT 
       @@targets.push STDERR.clone if t == STDERR
       break if t == STDOUT or t == STDERR  
       @@targets.push(File.open(t,'w+'))
    end
    self
  end

  def close
    @@targets.each {|t| f.close}
  end
end

STDOUT.reopen MultiIO.instance.setTargets(['/tmp/1.log',STDOUT,STDERR])
STDERR.reopen STDOUT 


threads = []

5.times.each do |i| 
  threads.push( 
    Thread.new do
      10000.times.each do |j|
        STDOUT.puts "out#{i}:#{j}"
      end
    end
  )
end

5.times.each do |i| 
  threads.push( 
    Thread.new do
      10000.times.each do |j|
        STDERR.puts "err#{i}:#{j}"
      end
    end
  )
end

threads.each {|t| t.join}

答案 3 :(得分:0)

来自matts的微小改进回答:

"""E"" is for evidence [sound recording] / by Sue Grafton." 05  8
"""F"" is for fugitive [sound recording] / by Sue Grafton." 05  6
"""G"" is for Grafton : the world of Kinsey Millhone / Natalie Hevener Kaufman and Carol McGinnis Kay." 06  1
"""G"" is for gumshoe [text (large print)] / Sue Grafton."  09,10  1
"""Galapagos"" means ""tortoises"" / written and illustrated by Ruth Heller."   10,04,09  2
"""Git on board 09  1
"""God's banker"" / by Rupert Cornwell."    05,10,11  1
"""Gospodi-- spasi i usmiri Rossi︠i︡u"" : Nikolaĭ II    10,11  1
"""H"" is for homicide [sound recording] / by Sue Grafton." 12  4