# Driving irb from IO.popen

**URL:** <https://www.ruby-forum.com/t/driving-irb-from-io-popen/191748>\
**Category:** Ruby\
**Created:** [July 17, 2010, 2:06pm UTC](https://www.ruby-forum.com/t/driving-irb-from-io-popen/191748 "2010-07-17T14:06:02Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jean-Julien\_F](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@Jean-Julien\_F](https://www.ruby-forum.com/u/Jean-Julien_F)\
**Post date:** [July 17, 2010, 2:06pm UTC](https://www.ruby-forum.com/t/driving-irb-from-io-popen/191748/1 "2010-07-17T14:06:02Z")

</div>

Hello,

I’m stuck with a problem concerning IO.popen  
It’s not necessarily related to irb but that’s a good point where to  
start.

I’d like to drive irb from ruby using IO.popen. The problem is: each  
time I type something inside irb, I can get one, two or more lines of  
output I’d like to store with no way to know beforehand how many there  
will be.

The thing is: I don’t know how to detect when irb has ended its output  
and is waiting for me to give some input.

I tried the following:

def irb(string)  
output = ‘’  
puts “Trying to talk to irb”  
IO.popen(‘irb --simple-prompt’,‘r+’) do |io|  
string.chomp.split.each do |s|  
io.puts s  
while line = io.gets  
line = io.gets  
puts line  
output += line  
end  
end  
end  
return output  
end

s = “2+2\n’2’+‘2’\n’2’+2”

irb(s)

I was hoping that io.gets would be nil when irb is waiting for input,  
but it’s not. So this little program gets stuck after the first  
statement:

brisingr ~/tmp\>ruby essai\_irb.rb  
Trying to talk to irb  
=\> 4

Rather, I would like him to return

> > 2+2  
> > =\> 4
> 
> > ‘2’+‘2’  
> > =\> “22”
> 
> > ‘2’+2  
> > TypeError: can’t convert Fixnum into String  
> > from (irb):4:in `+’  
> > from (irb):4

Does someone have a workaround ?

Thanks,

---

<div class="post-metadata">

**Author:** ![Jean-Julien\_F](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@Jean-Julien\_F](https://www.ruby-forum.com/u/Jean-Julien_F)\
**Post date:** [July 17, 2010, 2:39pm UTC](https://www.ruby-forum.com/t/driving-irb-from-io-popen/191748/2 "2010-07-17T14:39:24Z")

</div>

On 7/17/10, Jean-Julien F. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> The thing is: I don’t know how to detect when irb has ended its output  
> while line = io.gets
> 
> Rather, I would like him to return  
> Does someone have a workaround ?  
> You could try to detect whether irb is waiting based an what prompt it  
> prints. Alternately, you could use non-blocking io with a timeout to  
> determine when it is no longer printing anything to output. Neither  
> method is going to be a sure-fire way to determine when irb is  
> waiting, but you should be able to come up with something that works  
> well enough most of the time. You could even combine the 2 for greater  
> reliability.

---

<div class="post-metadata">

**Author:** ![Jean-Julien\_F](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@Jean-Julien\_F](https://www.ruby-forum.com/u/Jean-Julien_F)\
**Post date:** [July 17, 2010, 5:13pm UTC](https://www.ruby-forum.com/t/driving-irb-from-io-popen/191748/3 "2010-07-17T17:13:20Z")

</div>

On 07/17/2010 02:05 PM, Jean-Julien F. wrote:

> ```
> puts line
> output += line
> 
> ```

Note: \<\< is more efficient than += here.

> I was hoping that io.gets would be nil when irb is waiting for input,  
> =\> 4
> 
> > > ‘2’+‘2’  
> > > =\> “22”
> > 
> > > ‘2’+2  
> > > TypeError: can’t convert Fixnum into String  
> > > from (irb):4:in `+’  
> > > from (irb):4
> 
> Does someone have a workaround ?

Not a workaround but a solution: since you only want to collect the  
output of irb and do not need to react on it you can simply use a second  
thread that collects all the output.

def irb\_test(strings)  
puts “Trying to talk to irb”

```
 IO.popen('irb --simple-prompt','r+') do |io|
   rdr = Thread.new { io.read }

   io.puts strings

   io.close_write
   rdr.value
 end

```

end

Kind regards

robert

---

<div class="post-metadata">

**Author:** ![Jean-Julien\_F](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@Jean-Julien\_F](https://www.ruby-forum.com/u/Jean-Julien_F)\
**Post date:** [July 17, 2010, 7:06pm UTC](https://www.ruby-forum.com/t/driving-irb-from-io-popen/191748/4 "2010-07-17T19:06:53Z")

</div>

Hello Robert,

> Note: \<\< is more efficient than += here.

Yes. Bad habit on my side: I tend to find it more readable that way so  
I don’t use \<\< so often. I should rather use it more to find it easier  
to read :o)

Correct me if I’m wrong: \<\< will append to the existing String object  
whereas += will create a brand new String object by concatenating the  
two (the old version and what I want to add), so it could be less  
efficient if the string gets too long and/or if there are a lot of  
line to append ?

> Not a workaround but a solution: since you only want to collect the output  
> of irb and do not need to react on it you can simply use a second thread  
> that collects all the output.

Thanks a lot, it works perfectly !

I don’t need it now but as I have already been confronted to this  
issue (finding workarounds as I had access to the other program and  
could modify it), I’ll ask anyway: how would you tackle the problem if  
you did have to react to the output ? (just some hints would perfectly  
please me)

Thanks again,

---

<div class="post-metadata">

**Author:** ![Jean-Julien\_F](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@Jean-Julien\_F](https://www.ruby-forum.com/u/Jean-Julien_F)\
**Post date:** [July 18, 2010, 9:30am UTC](https://www.ruby-forum.com/t/driving-irb-from-io-popen/191748/5 "2010-07-18T09:30:39Z")

</div>

On 07/17/2010 06:43 PM, Jean-Julien F. wrote:

> two (the old version and what I want to add),  
> Correct.

> so it could be less  
> efficient if the string gets too long and/or if there are a lot of  
> line to append ?

I don’t think so. You have two cost factors: allocating the underlying  
memory to hold the sequence of characters (or bytes if you will) and the  
allocation of an object, registration with GC etc. The first cost  
factor needs to be paid regardless of variant used while the second  
costs factor is not incurred with the \<\< version. Memory allocation  
overhead is usually kept low by using a smart algorithm for calculating  
the size of the next memory chunk to allocate so allocations become  
rarer with more append operations of identical sized strings. IMHO the  
version with the single read that slurps in everything is even more  
efficient because you loop in C and not in Ruby. (Note that Ruby’s  
multithreading is not negatively affected; the single read does not  
block other threads.)

> please me)  
> There is expect (“ri IO#expect”). Documentation is a tad sparse but  
> there’s likely more to find on the web.

Kind regards

robert

---

<div class="post-metadata">

**Author:** ![Jean-Julien\_F](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@Jean-Julien\_F](https://www.ruby-forum.com/u/Jean-Julien_F)\
**Post date:** [July 19, 2010, 6:43pm UTC](https://www.ruby-forum.com/t/driving-irb-from-io-popen/191748/6 "2010-07-19T18:43:36Z")

</div>

> I don’t need it now but as I have already been confronted to this  
> issue (finding workarounds as I had access to the other program and  
> could modify it), I’ll ask anyway: how would you tackle the problem if  
> you did have to react to the output ? (just some hints would perfectly  
> please me)

You may want to check out a recently released gem called greenletters,

> **[Greenletters: Painless automation and testing for command-line applications -...](https://avdi.codes/greenletters-painless-automation-and-testing-for-command-line-applications/)**
>
> Did you ever use Expect to automate a complex command-line procedure, like an FTP upload? Expect is handy - and very powerful - but for Ruby projects it sure would be nice to be able to automate console apps directly from Ruby. You may not have known...
