Переназначение глобального $ stdout на консоль - ruby - PullRequest
5 голосов
/ 13 февраля 2012

Я пытаюсь установить $ stdout для временной записи в файл, а затем обратно в файл.

test.rb :
   old_stdout = $stdout    
    $stdout.reopen("mytestfile.out",'w+')
       puts "this goes in mytestfile"
    $stdout= old_stdout
puts "this should be on the console"
    $stdout.reopen("mytestfile1.out",'w+')
       puts "this goes in mytestfile1:"
    $stdout = old_stdout
 puts "this should be back on the console"

Вот вывод.

ruby test.rb => no output on the console
cat mytestfile.out 
  this goes in mytestfile
  this should be on the console
cat  mytestfile1.out
  this goes in mytestfile1:
  this should be back on the console

Я не уверен, почему $ stdout не сбрасывается в консоль?

Ответы [ 2 ]

7 голосов
/ 13 февраля 2012

Эта проблема может быть решена путем вызова dup на $stdout перед его изменением:

old_stdout = $stdout.dup  
$stdout.reopen("mytestfile.out",'w+')
puts "this goes in mytestfile"
$stdout = old_stdout.dup
puts "this should be on the console"
$stdout.reopen("mytestfile1.out",'w+')
puts "this goes in mytestfile1:"
$stdout = old_stdout
puts "this should be back on the console"

Выход:

ruby test.rb
# => this should be on the console
# => this should be back on the console
cat mytestfile.out
# => this goes in mytestfile
cat mytestfile1.out
# => this goes in mytestfile1

Вот как я обычно упаковываю эту функцию в функцию:

# Runs a block of code while blocking stdout.
# Note that /dev/null should be changed to NUL on Windows.
def silence_stdout(log = '/dev/null')
  old = $stdout.dup
  $stdout.reopen(File.new(log, 'w'))
  yield
  $stdout = old
end

Использование:

silence_stdout 'mytestfile.out' do
  puts "this goes in mytestfile"
end

puts "this should be on the console"

silence_stdout 'mytestfile1.out' do
  puts "this goes in mytestfile1"
end

puts "this should be back on the console"

Редактировать: как уже упоминалось, использование reopen необходимо только при работе с чистым кодом Ruby. Вышеприведенная функция работает как с чистым кодом Ruby, так и при использовании, например, расширений C, которые записывают в STDOUT.

3 голосов
/ 13 февраля 2012

Вам не нужно использовать reopen, если вы просто используете код Ruby.puts и другие методы Ruby будут использовать текущее значение $stdout, поэтому вы можете просто переназначить его.

old_stdout = $stdout    
$stdout = File.new("mytestfile.out",'w+')
puts "this goes in mytestfile"
$stdout = old_stdout
puts "this should be on the console"
$stdout = File.new("mytestfile1.out",'w+')
puts "this goes in mytestfile1:"
$stdout = old_stdout
puts "this should be back on the console"

Вам нужно использовать reopen, только если вы делаете что-то вроде создания дочерних процессов(например, с fork) и хотите, чтобы выходные данные дочернего элемента передавались в другое место, или если у вас есть расширение, которое записывает напрямую в стандартный вывод без использования Ruby's $stdout global.

В вашем коде при вызове reopen вы перенаправляете оба $stdout и old_stdout, так как они оба являются просто ссылками на один и тот же объект ввода-вывода, поэтому вы не получаете вывод обратно на консоль при назначении old_stdout назад к stdout.

...