How can I capture the output of NSLog in ruby? - ruby

I have a console application on a mac that puts out an error code via NSLog. How can I capture the results of NSLog when running the console application in ruby?
I've tried approaches like redirecting stderr, but that doesn't seem to do the trick.
OK, I will edit this to be crystal clear.
I have a program written for MacOS that currently reports its output via NSLog. For all intents and purposes, it can just have this line in its main.m file:
NSLog(#"Hello world!");
I want to capture the contents of that NSLog. I cannot change the program itself, I just have the log files. I want to do so in Ruby for the purposes of some rspec-based testing.
Right now, I cannot use redirects. Any kind of redirect, as in:
#output = `#{theProgramToTest}`
puts #output
results in no output. If I do the redirection of stderr as described in that previous question I linked to, I still have no result. So how can I capture the results of the program? I do not want to redirect them to a file.

Maybe this'll help:
require 'stringio'
str_stdout, str_stderr = (1..2).map{ StringIO.new }
puts "redirecting outputs"
old_stdout, old_stderr = $stdout, $stderr
$stdout, $stderr = str_stdout, str_stderr
STDOUT.puts "This is written to the old STDOUT"
STDERR.puts "This is written to the old STDERR"
$stdout.puts "This is written to str_stdout"
$stderr.puts "This is written to str_stderr"
puts 'this is output via "puts"'
`date`
`date >&2` # this is output to stderr
STDOUT.puts "resetting STDOUT and STDERR"
$stdout, $stderr = old_stdout, old_stderr
str_stdout.rewind
str_stderr.rewind
puts str_stdout.read
puts str_stderr.read
Which outputs:
redirecting outputs
This is written to the old STDOUT
This is written to the old STDERR
Mon Jul 8 21:51:19 MST 2013
resetting STDOUT and STDERR
This is written to str_stdout
this is output via "puts"
This is written to str_stderr
Single-step that using a debugger or PRY to see when the various outputs occur.

Related

Output redirection troubleshooting in Ruby

So I'm trying to redirect output to a file temporarily:
prev_std = STDOUT
$stdout.reopen(#filename, 'w:UTF-8')
# stuff happens here with #filename and lots of output
$stdout = prev_std
but when I do this, it seems as though I can't redirect it back afterwards. What am I doing wrong here?
STDOUT is a constant that represents the standard output, which is an IO object. And $stdout is a global variable whose default values is usually STDOUT itself, by default, both point to the same object:
$stdout.object_id == STDOUT.object_id => true
If you call a method on one of them, the other one will be affected. So if you call a method using $stdout it will also take effect on STDOUT. Calling reopen on $stdout will affect STDOUT as well (even if it's a constant).
If you want to redirect output to a file temporarily and restore it then to the standard output, you should assign $stdout a new IO object:
$stdout = File.open('/path/to/file', 'w:UTF-8')
puts "this will go to the file"
$stdout = STDOUT
puts "this will go to standard output!"
See also the following questions:
What is the difference between Ruby's $stdout and STDOUT?
What is the difference between STDIN and $stdin in Ruby?
Reopening an IO Stream vs. just using the new Stream
And The IO reopen method along with the rest of IO methods.
You're calling reopen on STDOUT since $stdout is just a global variable that points to the same object. This permanently changes STDOUT.
What about this:
$stdout = File.open(#filename, 'w:UTF-8')
# ...
$stdout = prev_std

Intercepting all puts and print statements from a Ruby program

I have legacy code, and rewriting this will take me a long while. Until then I need a solution that "works", even if it is ugly.
The main class of the code generates a long HTML string and stores this in an #instance variable.
Unfortunately, the larger framework also sometimes directly puts out something, through puts or print, and so can be used in a .cgi script.
I need to be able to capture all output from that framework, possibly filter or process it, before I send it to the user/visitor.
Is it possible to capture all puts and print statements from a Ruby script, and handle this gracefully?
In the final form, I will have to use puts and print anyway, but I need to sanitize some things there, possibly redirect, also optionally log output BEFORE I use puts/print.
Easy. By default puts and print output to the $stdout I/O channel. Reassign $stdout to a different file handle and output of those commands will go to the new channel.
Change where $stdout points.
File.open('spool.out', 'wb') do |fo|
$stdout = fo
puts 'hello world'
$stdout = STDOUT
end
Save that to a file and run it. You should see a file appear called "spool.out" containing "hello world".
It's not necessary to wrap everything in a File.open block. All that's important is you reassign $stdout to a file handle, then reset it later, so it could also be done like this:
$stdout = File.open('spool.out', 'wb')
puts 'hello world'
$stdout.close
$stdout = STDOUT
At startup, a Ruby script has access to a number of different global variables and constants: $stdout will be the same as STDOUT, and $stderr will be the same as STDERR.
See "Does Ruby use $stdout for writing the output of puts and return?" and "Putting the results of pp (or anything outputted to console) into a string" for more information.
You could redefine the print/puts function to add new functionality.
It could be done like so:
def print message
#do something, like editing the message and logging it
puts "Im here"
super "#{message}:edited version"
end
print "hello world"
Result:
->Im here
->hello world:edited version

How do I save the text of puts in Ruby to a txt file?

I wrote a madlib in Ruby, and want to save the resulting madlib to a txt file. This is what I wrote, but the resulting txt file is empty:
file=File.open("madlib_output.txt","a")
file.puts
file.close
There are ways to save the output of a script to a file without having to modify every puts in the script.
The easiest is to route the output at the command-line using redirection. Running a script with > some_file at the of the command will route all STDOUT to the file. Similarly, using > some_file 2>&1 will route both STDOUT and STDERR to the same file. This won't capture anything typed in at a gets as the code waits for input though, because that won't count as program output.
If you don't mind changing your code a little, you can temporarily change the interpreter's idea of what STDOUT is by reassigning it to a file:
old_stdout = $stdout
File.open('output.txt', 'w') do |fo|
$stdout = fo
# ----
# your code goes here
puts "hello world"
# ----
end
$stdout = old_stdout
Run that, then look at the file "output.txt" and you'll see "hello world", even though we didn't print to the file-handle fo directly, like we would normally do using fo.puts.
There are a variety of ways of doing the same thing but they amount to pointing STDOUT or STDERR somewhere else, writing to them, then resetting them.
Typically, if we intend from the start to output to a file, then we should use a File.open block:
File.open('output.txt', 'w') do |fo|
fo.puts "hello world"
end
The benefit of that is the file will be closed automatically when the block exits.
Is this what you looking for ? You can open madlib_output.txt file in append mode and whatever you want to write will be inside the block eg: "hi"
File.open("madlib_output.txt","a") do |f|
f.puts "hi"
end

Return output from "system" command in Ruby?

I have to execute a shell command from Ruby script but I have to retrieve the output so that I can use it in the script later on.
Here is my code:
output = system "heroku create" # => true
But the system command returns a boolean and not the output.
Simply said, system "heroku create" has to output to my screen (which it does) but also return the output so I can process it.
You could use
output = `heroku create`
See: http://ruby-doc.org/core/classes/Kernel.html
The Open3 library gives you full access to the standard IO streams (STDIN, STDOUT and STDERR).
It's part of Ruby, so there's no need to install a gem:
require 'open3'
stdin, stdout, stderr = Open3.popen3("heroku create")
puts stdout.read
stdin.close; stdout.close; stderr.close
or you can use the block form which closes the streams implicitely:
require 'open3'
Open3.popen3("heroku create") do |stdin, stdout, stderr|
puts stdout.read
end
See the Open3 documentation for the full details.
Edit: Added extra stream closing details. Thanks Christopher and Gregory.
You can use the following:
output = capture(:stdout) do
system("pwd") # your system command goes here
end
puts output
shortened version:
output = capture(:stdout) { system("pwd") }
Similarly we can also capture standard errors too with :stderr
capture method is provided by active_support/core_ext/kernel/reporting.rb
Looking at that library's code comments, capture is going to be deprecated, so not sure what is the current supported method name is.

Exposing console apps to the web with Ruby

I'm looking to expose an interactive command line program via JSON or another RPC style service using Ruby. I've found a couple tricks to do this, but im missing something when redirecting the output and input.
One method at least on linux is to redirect the stdin and stdout to a file then read and write to that file asynchronously with file reads and writes. Another method ive been trying after googling around was to use open4. Here is the code I wrote so far, but its getting stuck after reading a few lines from standard output.
require "open4"
include Open4
status = popen4("./srcds_run -console -game tf +map ctf_2fort -maxplayers 6") do |pid, stdin, stdout, stderr|
puts "PID #{pid}"
lines=""
while (line=stdout.gets)
lines+=line
puts line
end
while (line=stderr.gets)
lines+=line
puts line
end
end
Any help on this or some insight would be appreciated!
What I would recommend is using Xinetd (or similar) to run the command on some socket and then using the ruby network code. One of the problems you've already run into in your code here is that your two while loops are sequential, which can cause problems.
Another trick you might try is to re-direct stderr to stdout in your command, so that your program only has to read the stdout. Something like this:
popen4("./srcds_run -console -game tf +map ctf_2fort -maxplayers 6 2>&1")
The other benefit of this is that you get all the messages/errors in the order they happen during the program run.
EDIT
Your should consider integrating with AnyTerm. You can then either expose AnyTerm directly e.g. via Apache mod_proxy, or have your Rails controller act as the reverse proxy (handling authentication/session validation, then playing back controller.request minus any cookies to localhost:<AnyTerm-daemon-port>, and sending back as a response whatever AnyTerm replies with.)
class ConsoleController < ApplicationController
# AnyTerm speaks via HTTP POST only
def update
# validate session
...
# forward request to AnyTerm
response = Net::HTTP.post_form(URI.parse('http://localhost:#{AnyTermPort}/', request.params))
headers['Content-Type'] = response['Content-Type']
render_text response.body, response.status
end
Otherwise, you'd need to use IO::Select or IO::read_noblock to know when data is available to be read (from either network or subprocess) so you don't deadlock. See this too. Also check that either your Rails is used in a multi-threaded environment or that your Ruby version is not affected by this IO::Select bug.
You can start with something along these lines:
status = POpen4::popen4("ping localhost") do |stdout, stderr, stdin, pid|
puts "PID #{pid}"
# our buffers
stdout_lines=""
stderr_lines=""
begin
loop do
# check whether stdout, stderr or both are
# ready to be read from without blocking
IO.select([stdout,stderr]).flatten.compact.each { |io|
# stdout, if ready, goes to stdout_lines
stdout_lines += io.readpartial(1024) if io.fileno == stdout.fileno
# stderr, if ready, goes to stdout_lines
stderr_lines += io.readpartial(1024) if io.fileno == stderr.fileno
}
break if stdout.closed? && stderr.closed?
# if we acumulated any complete lines (\n-terminated)
# in either stdout/err_lines, output them now
stdout_lines.sub!(/.*\n/m) { puts $& ; '' }
stderr_lines.sub!(/.*\n/m) { puts $& ; '' }
end
rescue EOFError
puts "Done"
end
end
To also handle stdin, change to:
IO.select([stdout,stderr],[stdin]).flatten.compact.each { |io|
# program ready to get stdin? do we have anything for it?
if io.fileno == stdin.fileno && <got data from client?>
<write a small chunk from client to stdin>
end
# stdout, if ready, goes to stdout_lines

Resources