Intercepting output to STDERR - ruby

I have a Ruby application which- unexpectedly - from time to time outputs a lone line feed character to stderr. I would like to find the place in my program where this happens. Static analysis (grepping case-insensitively for stderr) did not reveal anything useful, i.e. while there were some STDERR.puts statements, they always output also something else besides the \n, so they can't be the culprit.
Does anybody have some idea how I can somehow catch every write to stderr, and if it happens, at least print a backtrace, so that I can see where this write originates?

If you need to trap all calls to a particular object there's a lot of utility in method_missing:
class Exploder
def method_missing(name, *args)
raise "Method #{name} was called!"
end
end
You can then substitute it and trap calls:
$stderr = Exploder.new
Any call like $stderr.write will now raise an exception.

Related

Running cmd commands through ruby

I am writing a program which execute an other program written in c, here is my first try
require 'Open3'
system 'tcc temp.c'
Open3.popen3('temp.exe') do |stdin, stdout, stderr|
stdin.puts '21\n'
STDOUT.puts stdout.gets
end
actual output:
Enter the temperature in degrees fahrenheit: The converted temperature is -6.11
desired output:
Enter the temperature in degrees fahrenheit: 21
The converted temperature is -6.11
and if you know a better way to do that please tell me, i am new to ruby.
You seem to have at least two potential issues:
Your newline will not expand inside single quotes. To include a newline within a string, you need to use double-quotes such as "21\n".
In some cases, you actually need a carriage return rather than a newline. This is especially true when trying to do Expect-like things with a terminal. For example, you may find you need \r instead of \n in your string.
You definitely need to fix the first thing, but you may need to try the second as well. This is definitely one of those "your mileage may vary" situations.
It seems like you're expecting 21 to appear on your screen because it does when you run temp.exe and type in 21. The reason it appears on your screen under those circumstances is that you're typing them into your shell, which "echoes" back everything you type.
When you run the program via Ruby, though, there's no shell and no typing, so 21 doesn't appear on your screen even though it's correctly being sent to the program's standard input.
The simplest solution is pretty simple. Just write it to Ruby's standard output as well:
require 'Open3'
system 'tcc temp.c'
Open3.popen3('temp.exe') do |stdin, stdout, stderr|
STDOUT.puts "21"
stdin.puts '"21"
STDOUT.puts stdout.gets
end
(You'll note that I took out \n—IO#puts adds that for you.)
This is a little repetitive, though. You might define a simple method to take care of it for you:
def echo(io, *args)
puts *args
io.puts *args
end
Then:
Open3.popen3('temp.exe') do |stdin, stdout, stderr|
echo(stdin, "21")
puts stdout.gets
end

Ruby : get output of external command even when there is no line break

I try to run an external command in Ruby, and parse its output .
IO.popen(command, :err=>[:child, :out]) {|ls_io|
ls_io.each do |line|
print line
end
}
This way of doing it works wonders… except when I parse the progress-output of a c-program that shows it progress to stdout with \r.
As long as the c-program has not outputted a \n (that is as long as it has not finished some long-operation), Ruby waits and sees nothing. Then when a \n is outputted, Ruby sees it all
1%\r2%\r3%\r…100%
task finished
I tried all of the many ways to call external commands (eg Calling shell commands from Ruby) ; but none seem to capture the progress. I also tried every opeartor such as STDOUT.sync = true, and the c-program does call fflush(stdout)
I finally found a workaroud. I do :
IO.popen(commande, :err=>[:child, :out]) {|ls_io|
while true
byte=ls_io.read(1)
if byte.nil?
break
end
print byte
end
}
It's stupid… but it works.
Any more elegant way, and much more efficient way to do this ? Performance is terrible, as if the "refresh rate" was slow.
Set the input record separator to "\r" right before your block (provided you know it in advance):
$/ = "\r"
Reference of global preset variables: http://www.zenspider.com/Languages/Ruby/QuickRef.html#pre-defined-variables

Catching command-line errors using %x

Whenever you want to execute something on the command line, you can use the following syntax:
%x(command to run)
However, I want to catch an error or at least get the response so I can parse it correctly. I tried setting:
result = %x(command to run)
and using a try-catch
begin
%x(command to run)
rescue
"didn't work"
end
to no avail. How can I capture the results instead of having them printed out?
So this doesn't directly answer your question (won't capture the command's output). But instead of trying begin/rescue, you can just check the exit code ($?) of the command:
%x(command to run)
unless $? == 0
"ack! error occurred"
end
Edit: Just remembered this new project. I think it does exactly what you want:
https://github.com/envato/safe_shell
You might want to redirect stderr to stdout:
result = %x(command to run 2>&1)
Or if you want to separate the error messages from the actual output, you can use popen3:
require 'open3'
stdin, stdout, stderr = Open3.popen3("find /proc")
Then you can read the actual output from stdout and error messages from stderr.
Here's how to use Ruby's open3:
require 'open3'
include Open3
stdin, stdout, stderr = popen3('date')
stdin.close
puts
puts "Reading STDOUT"
print stdout.read
stdout.close
puts
puts "Reading STDERR"
print stderr.read
stderr.close
# >>
# >> Reading STDOUT
# >> Sat Jan 22 20:03:13 MST 2011
# >>
# >> Reading STDERR
popen3 returns IO streams for STDIN, STDOUT and STDERR, allowing you to do I/O to the opened app.
Many command-line apps require their STDIN to be closed before they'll process their input.
You have to read from the returned STDOUT and STDERR pipes. They don't automatically shove content into a mystical variable.
In general, I like using a block with popen3 because it handles cleaning up behind itself.
Look through the examples in the Open3 doc. There's lots of nice functionality.
You need a mix of #Cam 's answer and #tonttu 's answer.
decent explanation of $? and others.
Edit: the domain http://blog.purifyapp.com is now in hands of a domain-squatter and scammer.
result = %x(command to run 2>&1)
unless $? == 0 #check if the child process exited cleanly.
puts "got error #{result}"
end

How to wait for process to finish using IO.popen?

I'm using IO.popen in Ruby to run a series of command line commands in a loop. I then need to run another command outside of the loop. The command outside of the loop cannot run until all of the commands in the loop have terminated.
How do I make the program wait for this to happen? At the moment the final command is running too soon.
An example:
for foo in bar
IO.popen(cmd_foo)
end
IO.popen(another_cmd)
So all cmd_foos need to return before another_cmd is run.
Apparently the canonical way to do this is:
Process.wait(popened_io.pid)
Use the block form and read all the content:
IO.popen "cmd" do |io|
# 1 array
io.readlines
# alternative, 1 big String
io.read
# or, if you have to do something with the output
io.each do |line|
puts line
end
# if you just want to ignore the output, I'd do
io.each {||}
end
If you do not read the output, it may be that the process blocks because the pipe connecting the other process and your process is full and nobody reads from it.
I think you'd need to assign the results from the IO.popen calls within the cycle to the variables, and keep calling read() on them until eof() becomes true on all.
Then you know that all the programs have finished their execution and you can start another_cmd.
for foo in bar
out = IO.popen(cmd_foo)
out.readlines
end
IO.popen(another_cmd)
Reading the output to a variable then calling out.readlines did it. I think that out.readlines must wait for the process to end before it returns.
Credit to Andrew Y for pointing me in the right direction.
I suggest you use Thread.join to synchronize the last popen call:
t = Thread.new do
for foo in bar
IO.popen(cmd_foo)
end
end
t.join
IO.popen(another_cmd)
Do you need the output of popen? If not, do you want to use Kernel#system or some other command?

Broken pipe (Errno::EPIPE)

i have a Broken pipe (Errno::EPIPE) error popping up and i don't understand what it is or how to fix it. the full error is:
example.rb:19:in `write': Broken pipe (Errno::EPIPE)
from example.rb:19:in `print'
from example.rb:19
line 19 of my code is:
vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
It means that whatever connection print is outputting to is no longer connected. Presumably the program began as input to some other program:
% ruby_program | another_program
What's happened is that another_program has exited sometime before the print in question.
Note:
The 1st section applies to Ruby scripts designed to act as terminal-based command-line utilities, assuming they require no custom handling or cleanup on receiving SIGPIPE, and assuming that you want them to exhibit the behavior of standard Unix utilities such as cat, which terminate quietly with a specific exit code when receiving SIGPIPE.
The 2nd section is for scripts that require custom handling of SIGPIPE, such as explicit cleanup and (conditional) output of error messages.
Opting into the system's default handling of SIGPIPE:
To complement wallyk's helpful answer and tokland's helpful answer:
If you want your script to exhibit the system's default behavior, as most Unix utilities (e.g., cat) do, use
Signal.trap("SIGPIPE", "SYSTEM_DEFAULT")
at the beginning of your script.
Now, when your script receives the SIGPIPE signal (on Unix-like systems), the system's default behavior will:
quietly terminate your script
report exit code 141 (which is calculated as 128 (indicating termination by signal) + 13 (SIGPIPE's number))
(By contrast, Signal.trap("PIPE", "EXIT") would report exit code 0, on receiving the signal, which indicates success.)
Note that in a shell context the exit code is often not apparent in a command such as ruby examble.rb | head, because the shell (by default) only reports the last command's exit code.
In bash, you can examine ${PIPESTATUS[#]} to see the exit codes of all commands in the pipeline.
Minimal example (run from bash):
ruby -e "Signal.trap('PIPE','SYSTEM_DEFAULT');(1..1e5).each do|i| puts i end" | head
The Ruby code tries to output 100,000 lines, but head only outputs the first 10 lines and then exits, which closes the read end of the pipe that connects the two commands.
The next time the Ruby code tries to the write end of that now broken pipe (after filling up the pipeline buffer), it triggers signal SIGPIPE, which terminates the Ruby process quietly, with exit code 141, which you can verify with echo ${PIPESTATUS[0]} afterwards.
By contrast, if you removed Signal.trap('PIPE','SYSTEM_DEFAULT'), i.e. with Ruby's default behavior, the command would break noisily (several lines of stderr output), and the exit code would be the nondescript 1.
Custom handling of SIGPIPE:
The following builds on donovan.lampa's helpful answer and adds an improvement suggested by
Kimmo Lehto, who points out that, depending on your script's purpose, receiving SIGPIPE shouldn't always terminate quietly, because it may indicate a legitimate error condition, notably in network code such as code for downloading a file from the internet.
He recommends the following idiom for that scenario:
begin
# ... The code that could trigger SIGPIPE
rescue Errno::EPIPE
# ... perform any cleanup, logging, ... here
# Raise an exception - which translates into stderr output -
# but only when outputting directly to a terminal.
# That way, failure is quiet inside a pipeline, such as when
# piping to standard utility `head`, where SIGPIPE is an expected
# condition.
raise if $stdout.tty?
# If the stack trace that the `raise` call results in is too noisy
# use something like the following instead, which outputs just the
# error message itself to stderr:
# $stderr.puts $! if $stdout.tty?
# Or, even simpler:
# warn $! if $stdout.tty?
# Exit with the usual exit code that indicates termination by SIGPIPE
exit 141
end
As a one-liner:
... rescue Errno::EPIPE raise if $stdout.tty?; exit 141
Note: Rescuing Errno::EPIPE works, because if the signal is ignored, the system call writing to the pipeline returns to the caller (instead of the caller process getting terminated), namely with standard error code EPIPE, which Ruby surfaces as exception Errno::EPIPE.
Although signal traps do work, as tokland said, they are defined application wide and can cause some unexpected behavior if you want to handle a broken pipe in some other way somewhere else in your app.
I'd suggest just using a standard rescue since the error still inherits from StandardError. More about this module of errors: http://ruby-doc.org/core-2.0.0/Errno.html
Example:
begin
vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
rescue Errno::EPIPE
puts "Connection broke!"
end
Edit: It's important to note (as #mklement0 does in the comments) that if you were originally piping your output using puts to something expecting output on STDOUT, the final puts in the code above will raise another Errno::EPIPE exception. It's probably better practice to use STDERR.puts anyway.
begin
vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
rescue Errno::EPIPE
STDERR.puts "Connection broke!"
end
#wallyk is right on the problem. One solution is to capture the signal with Signal.trap:
Signal.trap("PIPE", "EXIT")
If you are aware of some problem with this approach, please add a comment below.

Resources