puts "hi"
puts "bye"
我想将代码的标准输出(在这种情况下是 hi \nbye)存储到一个变量中,比如说‘result’,然后打印它。
puts result
我这样做的原因是将一个R代码集成到我的Ruby代码中,输出结果作为R代码运行时给予了STDOUT,但无法在代码中访问输出以进行一些评估。如果造成困惑,请见谅。因此,“puts result”一行应该给我“hi”和“bye”。
下面这个方法是一个方便的通用工具,可以将标准输出捕获并以字符串形式返回。(我经常在单元测试中使用它,以验证是否打印了某些内容。)请特别注意使用ensure
子句恢复$stdout(避免出现惊人的结果):
def with_captured_stdout
original_stdout = $stdout # capture previous value of $stdout
$stdout = StringIO.new # assign a string buffer to $stdout
yield # perform the body of the user code
$stdout.string # return the contents of the string buffer
ensure
$stdout = original_stdout # restore $stdout to its previous value
end
例如:
>> str = with_captured_stdout { puts "hi"; puts "bye"}
=> "hi\nbye\n"
>> print str
hi
bye
=> nil
Logger
捕获对原始$stdout
的引用,则此方法不一定适用。 - David Molesbegin
/end
匹配方法的持续时间,你可以跳过它们,只需将 ensure
放在方法的结尾即可(通常 ensure
/rescue
与方法的 def
和 end
在同一级别缩进)。 - Brian Underwoodbegin/end
语句是为了让新手更容易理解。像您这样的老手会知道在这种情况下可以省略begin/end。 - fearless_fooloriginal_stdout
,而不是 old_stdout
。 - Artur INTECH您可以将标准输出重定向到变量中。例如:
# Set up standard output as a StringIO object.
foo = StringIO.new
$stdout = foo
# Send some text to $stdout.
puts 'hi'
puts 'bye'
# Access the data written to standard output.
$stdout.string
# => "hi\nbye\n"
# Send your captured output to the original output stream.
STDOUT.puts $stdout.string
实际上,这可能不是一个好主意,但至少现在你知道它是可能的。
STDOUT.puts $stdout.string
来打印到原始输出流。 - Todd A. JacobsRake::Task["mytask"].invoke
并捕获结果以供以后使用的方法。我使用ensure
立即恢复了STDOUT
,以避免任何问题。如果被误用,这可能是危险的,但是当适度使用时它非常有效。 - Joshua Pinterresult = `./run-your-script`
puts result # will contain STDOUT from run-your-script
想了解在Ruby中运行子进程的更多信息,请访问这个Stack Overflow问题。
# capture_stream(stream) { block } -> String
#
# Captures output on +stream+ for both Ruby code and subprocesses
#
# === Example
#
# capture_stream($stdout) { puts 1; system("echo 2") }
#
# produces
#
# "1\n2\n"
#
def capture_stream(stream)
raise ArgumentError, 'missing block' unless block_given?
orig_stream = stream.dup
IO.pipe do |r, w|
# system call dup2() replaces the file descriptor
stream.reopen(w)
# there must be only one write end of the pipe;
# otherwise the read end does not get an EOF
# by the final `reopen`
w.close
t = Thread.new { r.read }
begin
yield
ensure
stream.reopen orig_stream # restore file descriptor
end
t.value # join and get the result of the thread
end
end
我从Zhon得到了灵感。
对于大多数实际应用,您可以将任何响应write
、flush
、sync
、sync=
和tty?
的内容放入$stdout
中。
在此示例中,我使用了stdlib中修改过的Queue。
class Captor < Queue
alias_method :write, :push
def method_missing(meth, *args)
false
end
def respond_to_missing?(*args)
true
end
end
stream = Captor.new
orig_stdout = $stdout
$stdout = stream
puts_thread = Thread.new do
loop do
puts Time.now
sleep 0.5
end
end
5.times do
STDOUT.print ">> #{stream.shift}"
end
puts_thread.kill
$stdout = orig_stdout
Minitest
版本:
assert_output
如果你需要确保是否生成了某些输出:
assert_output "Registrars processed: 1\n" do
puts 'Registrars processed: 1'
end
如果你确实需要捕获它,可以使用capture_io
:
out, err = capture_io do
puts "Some info"
warn "You did a bad thing"
end
assert_match %r%info%, out
assert_match %r%bad%, err
Minitest
本身可在任何Ruby版本中使用,从1.9.3
开始。
关于 RinRuby,请注意 R 有 capture.output
:
R.eval <<EOF
captured <- capture.output( ... )
EOF
puts R.captured
感谢@girasquid的回答。我将其修改为单文件版本:
def capture_output(string)
`echo #{string.inspect}`.chomp
end
# example usage
response_body = "https:\\x2F\\x2Faccounts.google.com\\x2Faccounts"
puts response_body #=> https:\x2F\x2Faccounts.google.com\x2Faccounts
capture_output(response_body) #=> https://accounts.google.com/accounts
rinruby
吗?我也试图捕获 rinruby (R) 的输出,但目前没有成功。 - knut