reading and writing to child process with streams in ruby

Discussion in 'Ruby' started by mpurdy, Apr 8, 2011.

  1. mpurdy

    mpurdy Guest

    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 :) 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
     
    mpurdy, Apr 8, 2011
    #1
    1. Advertising

  2. mpurdy

    Y. NOBUOKA Guest

    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)
     
    Y. NOBUOKA, Apr 8, 2011
    #2
    1. Advertising

  3. 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/.
     
    Brian Candler, Apr 8, 2011
    #3
  4. mpurdy

    mpurdy Guest

    On Apr 8, 4:03 am, "Y. NOBUOKA" <> 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 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:)
     
    mpurdy, Apr 9, 2011
    #4
  5. mpurdy

    mpurdy Guest

    On Apr 8, 9:32am, Brian Candler <> 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/.


    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 :)

    ----------------------------------------------------------------------
    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}"
     
    mpurdy, Apr 9, 2011
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Michael Grove
    Replies:
    5
    Views:
    2,249
    Michael Grove
    Aug 23, 2005
  2. Jeff Rodriguez
    Replies:
    23
    Views:
    1,211
    David Schwartz
    Dec 9, 2003
  3. Leslaw Bieniasz
    Replies:
    2
    Views:
    1,086
    Thomas J. Gritzan
    Jan 15, 2010
  4. john
    Replies:
    7
    Views:
    154
  5. Z W
    Replies:
    0
    Views:
    197
Loading...

Share This Page