web-dev-qa-db-fra.com

Comment capturer STDOUT sur une chaîne?

puts "hi"
puts "bye"

Je veux stocker le STDOUT du code jusqu'à présent (dans ce cas, salut\nbye dans une variable, dites "résultat" et imprimez-le)

puts result

La raison pour cela est que j’ai intégré un code R dans mon code Ruby, dont la sortie est transmise à STDOUT pendant l’exécution du code R, mais il n’est pas possible d’accéder à la sortie dans le code pour effectuer certaines évaluations. Désolé si c'est déroutant. Donc, la ligne "met le résultat" devrait me donner salut et au revoir.

34
script_kiddie

Rediriger la sortie standard vers un objet StringIO

Vous pouvez certainement rediriger la sortie standard vers une variable. Par exemple:

# 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

En pratique, ce n'est probablement pas une bonne idée, mais au moins maintenant, vous savez que c'est possible.

40
Todd A. Jacobs

Une fonction pratique pour capturer stdout dans une chaîne ...

La méthode suivante est un outil pratique très utile pour capturer stdout et le renvoyer sous forme de chaîne. (J'utilise cela fréquemment dans les tests unitaires où je veux vérifier quelque chose imprimé sur stdout.) Notez en particulier l'utilisation de la clause ensure pour restaurer $ stdout (et éviter les surprises):

def with_captured_stdout
  original_stdout = $stdout
  $stdout = StringIO.new
  yield
  $stdout.string
ensure
  $stdout = original_stdout
end

Donc, par exemple:

>> str = with_captured_stdout { puts "hi"; puts "bye"}
=> "hi\nbye\n"
>> print str
hi
bye
=> nil
57
fearless_fool

Si ActiveSupport est disponible dans votre projet, vous pouvez procéder comme suit:

output = capture(:stdout) do
  run_arbitrary_code
end

Plus d'informations sur Kernel.capture peuvent être trouvées ici

9
Dimitris Zorbas

Vous pouvez le faire en appelant votre script R à l'intérieur de butées, comme ceci:

result = `./run-your-script`
puts result  # will contain STDOUT from run-your-script

Pour plus d'informations sur l'exécution de sous-processus dans Ruby, consultez cette question de débordement de pile .

8
girasquid

Dans la plupart des cas, vous pouvez insérer n'importe quoi dans $stdout qui répond à write, flush, sync, sync= et tty?

Dans cet exemple, j'utilise un Queue modifié à partir de stdlib.

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

Vous avez besoin de quelque chose comme ceci si vous voulez agir activement sur les données et ne pas simplement les regarder une fois la tâche terminée. Utiliser StringIO ou un fichier sera problématique avec plusieurs threads essayant de synchroniser les lectures et les écritures simultanément.

1
Kimmo Lehto

Minitest versions:


assert_output si besoin de s’assurer que des sorties sont générées:

assert_output "Registrars processed: 1\n" do
  puts 'Registrars processed: 1'
end

assert_output


ou utilisez capture_io si vous avez vraiment besoin de le capturer:

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

capture_io

Minitest lui-même est disponible dans toutes les versions de Ruby à partir de 1.9.3

0
Artur Beljajev

Pour RinRuby, sachez que R a capture.output:

R.eval <<EOF
captured <- capture.output( ... )
EOF

puts R.captured 
0
Koert Vrieswijk