Collecting Exe printf outputs from system call in Ruby - ruby

I want to store the printf output of an exe file that I call by system(foo.exe) in ruby and get the exit status. How would I do that?

Open3 is the best way to do this.
require 'open3'
Open3.popen3('foo.exe') do |stdin, stdout, stderr, thr|
status = thr.value
output = stdout.read
errors = stderr.read
end

Related

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

ruby - IO.popen not working lame stdin and stdout encoding

I've been working with pipes and IO.popen specifically in Ruby and have come across a problem that I can't figure out. I am trying to write binary data from the flac process to the lame process into a file. The code structure I am using is below.
# file paths
file = Pathname.new('example.flac').realpath
dest = Pathname.new('example.mp3')
# execute the process and return the IO object
wav = IO.popen("flac --decode --stdout \"#{file}\"", 'rb')
lame = IO.popen("lame -V0 --vbr-new - -", 'r+b')
# write output from wav to the lame IO object
lame << wav.read
# close pipe for writing (should indicate to the
# process that input for stdin is finished).
lame.close_write
# open up destiniation file and write from lame stdout
dest.open('wb'){|out|
out << lame.read
}
# close all pipes
wav.close
lame.close
However, it doesn't work. After flac has run, the script hangs and lame remains idle (no processor usage at all). No errors or exceptions occur.
I am using cygwin on Windows 7, with the cygwin ruby package (1.9.3p429 (2013-05-15) [i386-cygwin]).
I must be doing something wrong, any help is much appreciated. Thanks!
EXTRA #1
I am wanting to pipe in and out the binary data from the lame process because I am trying to create a platform independent (ruby support limited of course) to transcode audio files, and the Windows binary of lame only supports Windows' path names, and not cygwin's.
EDIT #1
I read in some places (I did not save the URLs, I'll try looking for them in my browser history) that IO.popen has known issues with blocking processes in Windows and that this could be the case.
I have played around with other libraries including Ruby's Open3.popen3 and Open4, however following a very similar code structure to the one above, the lame process still hangs and remains unresponsive.
EDIT #2
I found this article which talked about the limitations of of Windows's cmd.exe and how it prevents the use of streamed data from files to stdin.
I refactored my code to look like shown below to test this out, and as it turns out, lame freezes on stdin write. If I removed (comment out) that line, the lame process executes (with an 'unsupported audio format' warning). Perhaps what the article said could explain my problem here.
# file paths
file = Pathname.new('example.flac').realpath
dest = Pathname.new('example.mp3')
# some local variables
read_wav = nil
read_lame = nil
# the flac process, which exits succesfully
IO.popen("flac --decode --stdout \"#{file}\"", 'rb'){|wav|
until wav.eof do
read_wav = wav.read
end
}
# the lame process, which fails
IO.popen("lame -V0 --vbr-new --verbose - -", 'r+b'){|lame|
lame << read_wav # if I comment out this, the process exits, instead of hanging
lame.close_write
until lame.eof do
read_lame << lame.read
end
}
EDIT #3
I found this stackoverflow which (in the first answer) mentioned that cygwin pipe implementation is unreliable. This could perhaps not actually be related to Windows (at least not directly) but instead to cygwin and its emulation. I have instead opted to use the following code, based upon icy's answer, which works!
flac = "flac --decode --stdout \"#{file}\""
lame = "lame -V0 --vbr-new --verbose - \"#{dest}\""
system(flac + ' | ' + lame)
Did you try the pipe | character?
Tested this on windows with ruby installer
require 'open3'
command = 'dir /B | sort /R' # a windows example command
Open3.popen3(command) {|stdin, stdout, stderr, wait_thr|
pid = wait_thr.pid
puts stdout.read #<a list of files in cwd in reverse order>
}
Other ways: Ruby pipes: How do I tie the output of two subprocesses together?
EDIT:
using IO::pipe
require 'open3'
command1 = 'dir /B'
command2 = 'sort /R'
reader,writer = IO.pipe
Open3.popen3(command1) {|stdin, stdout, stderr, wait_thr|
writer.write stdout.read
}
writer.close
stdout, stderr, status = Open3.capture3(command2, :stdin_data => reader.read)
reader.close
puts "status: #{status}" #pid and exit code
puts "stderr: #{stderr}" #use this to debug command2 errors
puts stdout
Embedding the two also appears to work, yet, as the blog you referred to said, one must wait for the first command to finish (not real-time -- test with a ping command)
stdout2 = ''
Open3.popen3(command1) {|stdin, stdout, stderr, wait_thr|
stdout2, stderr2, status2 = Open3.capture3(command2, :stdin_data => stdout.read)
}
puts stdout2
Use pipeline from Open3:
require "open3"
wavCommand = "flac --decode --stdout \"#{file}\""
lameCommand = "lame -V0 --vbr-new - -"
Open3.pipeline(wavComamnd, lameCommand)
The last line spawns two processes and connects stdout of the first process to stdin of the second one. Alternatively you can have access to stdin of the first process using pipeline_w or you can obtain stdout of the last command using pipeline_r or you can have both, using pipline_rw.

How can I capture the output of NSLog in 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.

Forming sanitary shell commands or system calls in Ruby

I'm building a daemon that will help me manage my server(s). Webmin works fine, as does just opening a shell to the server, but I'd prefer to be able to control server operations from a UI I design, and also expose some functionality to end users.
The daemon will pick up actions from a queue and execute them. However, since I'll be accepting input from users, I want to make sure they're not permitted to inject something dangerous into a privileged shell command.
Here's a fragment that exemplifies my problem:
def perform
system "usermod -p #{#options['shadow']} #{#options['username']}"
end
A gist that explains more: https://gist.github.com/773292
I'm not positive if typical escaping and sanitizing of inputs is enough for this case, and being a designer, I don't have a ton of security-related experience. I know that this is something that should probably be obvious to me, but its not!
How can I ensure that the web application that will create and serialize the actions can't pass dangerous text into the privileged process that receives the actions?
Thanks for the help
arb
It doesn't look like you need a shell for what you're doing. See the documentation for system here: http://ruby-doc.org/core/classes/Kernel.html#M001441
You should use the second form of system. Your example above would become:
system 'usermod', '-p', #options['shadow'], #options['username']
A nicer (IMO) way to write this is:
system *%W(usermod -p #{#options['shadow']} #{#options['username']})
The arguments this way are passed directly into the execve call, so you don't have to worry about sneaky shell tricks.
If you need not just the exit status but also the result you probably want to use Open3.popen3:
require 'open3'
stdin, stdout, stderr = Open3.popen3('usermod', '-p', #options['shadow'], #options['username'])
stdout.gets
sterr.gets
More information here: Getting output of system() calls in Ruby
I'd suggest looking into the 'shellwords' module. This script:
require 'shellwords'
parts = ['echo', "'hello world'; !%& some stuff", 'and another argument']
command = Shellwords.shelljoin( parts )
puts command
output = `#{ command }`
puts output
outputs the escaped text and the expected output:
echo \'hello\ world\'\;\ \!\%\&\ some\ stuff and\ another\ argument
'hello world'; !%& some stuff and another argument
This is an old question, but since it's pretty much the only real answer you'll find when googling I thought I'd add a caveat. The multi argument version of system seems reasonably safe on Linux, but it is NOT on Windows.
Try system "dir", "&", "echo", "hi!"
on a Windows system. Both dir and echo will be run. Echo could of course just as well be something far less innocuous.
I know this is an old thread, but there is another option that was lightly touched on by Simon Hürlimann.
There is not a lot of information about this topic and I think this might help others in need.
For this example we'll use Open3 which gives you the ability to run commands synchronously or asynchronously, and provides stdout, stderr, exit codes, and PID.
Open3 grants you access to stdout, stderr, exit codes and a thread to wait for the child process when running another program. You can specify various attributes, redirections, current directory, etc., of the program in the same way as for Process.spawn. (Source: Open3 Docs)
I chose to format the output as a CommandStatus object. This contains our stdout, stderr, pid (Of the worker thread) and exitstatus.
class Command
require 'open3'
class CommandStatus
#stdout = nil
#stderr = nil
#pid = nil
#exitstatus = nil
def initialize(stdout, stderr, process)
#stdout = stdout
#stderr = stderr
#pid = process.pid
#exitstatus = process.exitstatus
end
def stdout
#stdout
end
def stderr
#stderr
end
def exit_status
#exitstatus
end
def pid
#pid
end
end
def self.execute(command)
command_stdout = nil
command_stderr = nil
process = Open3.popen3(ENV, command + ';') do |stdin, stdout, stderr, thread|
stdin.close
stdout_buffer = stdout.read
stderr_buffer = stderr.read
command_stdout = stdout_buffer if stdout_buffer.length > 0
command_stderr = stderr_buffer if stderr_buffer.length > 0
thread.value # Wait for Process::Status object to be returned
end
return CommandStatus.new(command_stdout, command_stderr, process)
end
end
cmd = Command::execute("echo {1..10}")
puts "STDOUT: #{cmd.stdout}"
puts "STDERR: #{cmd.stderr}"
puts "EXIT: #{cmd.exit_status}"
While reading the STDOUT/ERR buffers, I use command_stdout = stdout_buffer if stdout_buffer.length > 0 to control whether the command_stdout variable is assigned or not. You should pass nil instead of "" when no data is present. It's more clear when handing data later on.
You probably noticed me using command + ';'. The reason for this is based on the documentation from Kernel.exec (Which is what popen3 uses):
If the string from the first form (exec("command")) follows these
simple rules:
no meta characters
no shell reserved word and no special built-in
Ruby invokes the command directly without shell
You can force shell invocation by adding ";" to the string (because
";" is a meta character)
This simply prevents a Ruby from throwing a 'spawn': No such file or directory error if you pass a malformed command. Instead it will pass it straight to the kernel where the error will be resolved gracefully and appear as STDERR instead of an uncaught exception.
Modern, secure and simple solution (popen will escape arguments for you):
IO.popen(['usermod', '-p', #options['shadow'], #options['username']]).read
(#read will close the IO before returning)

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.

Resources