PTY.spawn prints incorrect output and hangs - ruby

I'm trying to use PTY to run commands from my Ruby program. A very basic example is below:
require "pty"
PTY.spawn("wc", "-l") do |r, w, pid|
w.write("one\ntwo\nthree\n")
w.close
while !r.eof?
begin
data = r.read_nonblock(1024)
print(data)
rescue IO::WaitReadable
IO.select([r])
end
end
end
My intention with this code is to run wc, write three lines to wc's stdin, and then read the output from PTY's output stream. However, this is what I see as the program output
> bundle exec ruby wc_test.rb
one
two
three
As you can see, the data written to w is read back through r, which seems wrong. After the output, the program hangs. If I remove the print(data) line, then there is no output from the program, but it still hangs.

Related

How do I pass an angle bracket ("<") to IO.popen in Ruby?

This command works fine when using backticks to make a system call:
aspell -a < /path/textfile
However, if I try this it does not work:
result = IO.popen(["aspell", "-a", "<", "/path/textfile"]).read
It seems to be an issue with the angle bracket, because this works fine:
result = IO.popen([ "aspell", "--help"]).read
What am I doing wrong here?
That's a shell operation, and as popen is interfacing directly with your command, you can't do it. Instead you assume the responsibility for doing this, which is why the Open3 library exists and things like the popen2 method in particular:
Adapting your code:
require 'open3'
Open3.popen2('aspell', '-a') do |stdin, stdout, wait_thr|
# Feed the contents of /path/textfile into the STDIN of this
# subprocess.
stdin.write(File.read('/path/textfile'))
stdin.close
# Read the results back
result = stdout.read
end
This might be advantageous since you no longer need to write to a temporary file to do operations like this, you can just feed in data directly through the stdin pipe.

Evolution e-mail client, pipe to program, code always returns 0

I am using "pipe to program" option in Evolution email client, that runs following ruby script
#!/usr/bin/ruby
%% example code below
junk_mail = 2
junk_mail
Now this program always returns 0, irrespective of what the value of junk_mail variable is.
I guess it has something to do with Evolution forking a child process to run this code, and always 0 (clean exit) is received back?
Help needed.
EDIT
I figured out actual problem is with data being read from pipe. Following code works fine when tested in command line, but it is unable to read pipe data when called from Evolution client
#!/usr/bin/ruby
email_txt = ARGF.read
File.open("~/debug.txt", 'a') { |file| file.write(email_txt + "\n") }
$cat email.txt | ./myprog.rb
This gives debug.txt as expected, but when called from Evolution pipe-to-program, it gives empty data.
Am I using the correct way to read piped stream data when called from external program? (I am under Fedora 20).
Use exit:
#!/usr/bin/ruby
junk_mail = 2
exit junk_mail
You can test this by running it from the command line in linux, then echoing the exit value via
echo $?
EDIT
To read STDIN into a single string:
email_txt = STDIN.readlines.join

Calling a Perl script from Ruby

I am currently attempting to figure out a way to call a Perl script from Ruby and have it output as if I was in the terminal and would allow me to provide input if it is needed.
I have figured out how I can do this and get the input after the fact but because the Perl script is still running, I am not able to run anything else.
I should note that I can not edit the Perl scripts. These scripts are being provided and this Ruby script is being made to make the process of running all of the Perl scripts easier and ensuring they are in the right order.
upgradestatus = `#{upgradearray[arraylocation]}`
This would be the relevant part my code for this. I have attempted a few other variations of how to do this but I get the same situation every time. When the script starts running it requires input so it just sits there.
You can't do what you want using backticks, %x or as a normal sub-shell, because they lack the ability to watch the output of the sub-command's output.
You could do it using Open3's popen2 or popen3 methods. They let you send to the STDIN stream for the called program, and receive data from the STDOUT. popen3 also lets you see/capture the STDOUT stream too. Unfortunately, often you have to send, then close the STDIN stream before the called program will return its information, which might be the case of the Perl scripts.
If you need more control, look into using Ruby's built-in Pty module. It's designed to let you talk to a running app through a scripting mechanism. You have to set up code to look for prompts, then respond to them by sending back the appropriate data. It can be simple, or it can be a major PITA, depending on the code you're talking to.
This is the example for the open command:
PTY.open {|m, s|
p m #=> #<IO:masterpty:/dev/pts/1>
p s #=> #<File:/dev/pts/1>
p s.path #=> "/dev/pts/1"
}
# Change the buffering type in factor command,
# assuming that factor uses stdio for stdout buffering.
# If IO.pipe is used instead of PTY.open,
# this code deadlocks because factor's stdout is fully buffered.
require 'io/console' # for IO#raw!
m, s = PTY.open
s.raw! # disable newline conversion.
r, w = IO.pipe
pid = spawn("factor", :in=>r, :out=>s)
r.close
s.close
w.puts "42"
p m.gets #=> "42: 2 3 7\n"
w.puts "144"
p m.gets #=> "144: 2 2 2 2 3 3\n"
w.close
# The result of read operation when pty slave is closed is platform
# dependent.
ret = begin
m.gets # FreeBSD returns nil.
rescue Errno::EIO # GNU/Linux raises EIO.
nil
end
p ret #=> nil

How do you pipe output from a Ruby script to 'head' without getting a broken pipe error

I have a simple Ruby script that looks like this
require 'csv'
while line = STDIN.gets
array = CSV.parse_line(line)
puts array[2]
end
But when I try using this script in a Unix pipeline like this, I get 10 lines of output, followed by an error:
ruby lib/myscript.rb < data.csv | head
12080450
12080451
12080517
12081046
12081048
12081050
12081051
12081052
12081054
lib/myscript.rb:4:in `write': Broken pipe - <STDOUT> (Errno::EPIPE)
Is there a way to write the Ruby script in a way that prevents the broken pipe exception from being raised?
head is closing the standard output stream after it has read all the data it needs. You should handle the exception and stop writing to standard output. The following code will abort the loop once standard output has been closed:
while line = STDIN.gets
array = CSV.parse_line(line)
begin
puts array[2]
rescue Errno::EPIPE
break
end
end
The trick I use is to replace head with sed -n 1,10p.
This keeps the pipe open so ruby (or any other program that tests for broken pipes and complains) doesn't get the broken pipe and therefore doesn't complain. Choose the value you want for the number of lines.
Clearly, this is not attempting to modify your Ruby script. There almost certainly is a way to do it in the Ruby code. However, the 'sed instead of head' technique works even where you don't have the option of modifying the program that generates the message.

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?

Resources