Reading and writing to child process with streams in ruby

i am trying to fork a process to run a simple script which requires a
username and password. i made a test case with a simple bash script
and a simple ruby script; however, the ruby script hangs in
stdout.read???

i am new to ruby, so i am assuming i am doing something wrong :slight_smile: can
anyone help?

路路路

----------------------------------------------------------------------
bash script
----------------------------------------------------------------------
#! /bin/bash
sleep 3
echo -n "username: "
read -e USERNAME
echo -n "password: "
read -e PASSWORD
echo "running your script now for $USERNAME with password $PASSWORD"
echo 'doing something...'
sleep 3
echo 'end doing something...quiting'
exit 0

----------------------------------------------------------------------
ruby script
----------------------------------------------------------------------
require 'open3'

cmd = "./myScript.bash"
#cmd = "gpg --list-keys"
begin
聽聽聽stdin, stdout, stderr = Open3.popen3 cmd
聽聽聽done = 0
聽聽聽until done == 1
聽聽聽聽聽聽begin
聽聽聽聽聽聽聽聽聽line = stdout.read
聽聽聽聽聽聽聽聽聽print "#{line}"
聽聽聽聽聽聽聽聽聽puts line.eql?("username: ")
聽聽聽聽聽聽聽聽聽if line.eql?("username: ")
聽聽聽聽聽聽聽聽聽聽聽聽puts "myuser"
聽聽聽聽聽聽聽聽聽聽聽聽stdin.write "myuser\n"
聽聽聽聽聽聽聽聽聽聽聽聽line = stdout.read
聽聽聽聽聽聽聽聽聽聽聽聽if line.eql?("password: ")
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽puts "mypassword"
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽stdin.write "mypassword\n"
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽done = 1

聽聽聽聽聽聽聽聽聽聽聽聽end
puts "im here!"

聽聽聽聽聽聽聽聽聽end

聽聽聽聽聽聽end

聽聽聽end

end

Hi,

Using IO#readpartial [1] instead of IO#read, you'll see the ruby
script run as expected.
However, I don't know if this is the best way or not.

[1] http://www.ruby-doc.org/core/classes/IO.html#M000917

路路路

----------------------------------------------------------------------
ruby script
----------------------------------------------------------------------
require 'open3'

cmd = "./myScript.bash"
#cmd = "gpg --list-keys"
begin
聽聽stdin, stdout, stderr = Open3.popen3 cmd

聽聽p stdin, stdout, stderr
聽聽done = 0
聽聽until done == 1
聽聽聽聽begin
聽聽聽聽聽聽# using IO#readpartial instead of IO#read
聽聽聽聽聽聽line = stdout.readpartial( 4096 )
聽聽聽聽聽聽puts "#{line}"
聽聽聽聽聽聽puts line.eql?("username: ")
聽聽聽聽聽聽if line.eql?("username: ")
聽聽聽聽聽聽聽聽puts "myuser"
聽聽聽聽聽聽聽聽stdin.write "myuser\n"
聽聽聽聽聽聽聽聽# using IO#readpartial instead of IO#read
聽聽聽聽聽聽聽聽line = stdout.readpartial( 4096 )
聽聽聽聽聽聽聽聽if line.eql?("password: ")
聽聽聽聽聽聽聽聽聽聽puts "mypassword"
聽聽聽聽聽聽聽聽聽聽stdin.write "mypassword\n"
聽聽聽聽聽聽聽聽聽聽done = 1
聽聽聽聽聽聽聽聽end
聽聽聽聽聽聽聽聽puts "im here!"
聽聽聽聽聽聽end
聽聽聽聽rescue => err
聽聽聽聽聽聽p err
聽聽聽聽end
聽聽end
end

--
淇″病 瑁曚篃 (NOBUOKA Yuya)

mpurdy wrote in post #991644:

echo -n "username: "

You are sending just "username: " without a trailing newline.

         line = stdout.read

Here you are reading from stdout until the end-of-file (i.e. until the
other side terminates or closes the file). This will wait forever.

Two possible solutions:

1. Change your shell script to send data ending with a newline, then use
'gets' in the ruby code.

2. More generally, use expect.rb in the standard library. Then you can
wait for a particular sequence of characters, e.g. /name: /, before
continuing.

HTH,

Brian.

路路路

--
Posted via http://www.ruby-forum.com/\.

thanx that did work; however, i after doing more research i agree this
is not the best way. i am a c/c++, java/groovy guy...this ruby stuff
is all new to me; however, after day one; i like it:-)

路路路

On Apr 8, 4:03 am, "Y. NOBUOKA" <nobu...@r-definition.com> wrote:

Hi,

Using IO#readpartial [1] instead of IO#read, you'll see the ruby
script run as expected.
However, I don't know if this is the best way or not.

[1]http://www.ruby-doc.org/core/classes/IO.html#M000917

----------------------------------------------------------------------
ruby script
----------------------------------------------------------------------
require 'open3'

cmd = "./myScript.bash"
#cmd = "gpg --list-keys"
begin
  stdin, stdout, stderr = Open3.popen3 cmd

  p stdin, stdout, stderr
  done = 0
  until done == 1
    begin
      # using IO#readpartial instead of IO#read
      line = stdout.readpartial( 4096 )
      puts "#{line}"
      puts line.eql?("username: ")
      if line.eql?("username: ")
        puts "myuser"
        stdin.write "myuser\n"
        # using IO#readpartial instead of IO#read
        line = stdout.readpartial( 4096 )
        if line.eql?("password: ")
          puts "mypassword"
          stdin.write "mypassword\n"
          done = 1
        end
        puts "im here!"
      end
    rescue => err
      p err
    end
  end
end

--
淇″病 瑁曚篃 (NOBUOKA Yuya)

thanx, i looked into it and rewrote the script using the IO.expect and
it worked good. here is my scripts now:

(note: this is just to have an example for myself and team; we will
using ssh so all the passwords will be protected :slight_smile:

路路路

On Apr 8, 9:32 am, Brian Candler <b.cand...@pobox.com> wrote:

mpurdy wrote in post #991644:

> echo -n "username: "

You are sending just "username: " without a trailing newline.

> line = stdout.read

Here you are reading from stdout until the end-of-file (i.e. until the
other side terminates or closes the file). This will wait forever.

Two possible solutions:

1. Change your shell script to send data ending with a newline, then use
'gets' in the ruby code.

2. More generally, use expect.rb in the standard library. Then you can
wait for a particular sequence of characters, e.g. /name: /, before
continuing.

HTH,

Brian.

--
Posted viahttp://www.ruby-forum.com/.

----------------------------------------------------------------------
bash script
----------------------------------------------------------------------
#! /bin/bash
echo -n "username: "
read -e USERNAME
echo -n "password: "
read -e PASSWORD
echo "running your script now for $USERNAME with password $PASSWORD"
echo 'doing something...'
sleep 1
echo 'end doing something...quiting'
exit 0

----------------------------------------------------------------------
ruby script
----------------------------------------------------------------------
require 'pty' # found in ruby source @ /ext/pty
require 'expect' # found in ruby source @ /ext/pty/lib/expect.rb

#buffer stores the output from the child process
buffer = ""

# spawn a child process (fork)
PTY.spawn("./myScript.bash") do |output, input, pid|
   input.sync = true

   #set the expect verbosity flag to false or you will get output from
expect
   $expect_verbose = false

   #get user from environment if posible
   if !ENV['USER'].nil?
      username = ENV['USER']
   else
      username = 'guest'
   end

   #expect the username prompt and return the username
   output.expect('username: ') do
      input.puts(username)
   end

   #expect the password prompt and return the password
   output.expect('password: ') do
     input.puts 'thePassword'
   end

   #throw away the password comming back to thru the output (note: you
still get the '\n')
   output.expect('thePassword') do
   end

   #read all the output from the process and put it in a buffer for
later
   #keep reading until the EOFError exception is thrown
   done = 0
   while done == 0
      begin
         buffer += output.readpartial(1024)
      rescue EOFError
         done = 1
      end

   end

end

puts "myExcept.rb script ran myScript.bash the results are: \n
#{buffer}"