Piping input when shelling out

Hi all,

I want to use Kernel#system or one of its friends to manipulate some
data I have in a ruby string. Ideally, I’d want the executed command
to read the string from stdin, as if ruby had piped the data to it.
The data may be reasonably large. Any tips?

thanks

Caio C. wrote:

Hi all,

I want to use Kernel#system or one of its friends to manipulate some
data I have in a ruby string. Ideally, I’d want the executed command
to read the string from stdin, as if ruby had piped the data to it.
The data may be reasonably large. Any tips?

Could you be a bit more clear? Do you want to write a program that will
cooperate with a stream? As in:

data_source | ruby-program | data_dest

Yes?

On 2006-09-19, at 21:45 , Paul L. wrote:

Could you be a bit more clear? Do you want to write a program that
will
cooperate with a stream? As in:

data_source | ruby-program | data_dest

Yes?

No, from within a ruby program I want to call some shell command to
act on data stored in a ruby string, so, in the ruby program,
something like, in pseudoruby:

output = system(‘shell_cmd --params etc’, :input => a_string)

On 2006-09-19, at 21:51 , Daniel N wrote:

You could try using IO#popen The docs are at

class IO - RDoc Documentation

Hope this is what your after.

Looks like it. Thanks a lot.

Do you know if it places the exit code in $? ?

I wonder what happens if the subprocess is killed by the system?

I guess I’ll run some tests, but if you happen to know the answers,
I’d be glad to hear them nonetheless.

On 9/20/06, Caio C. [email protected] wrote:

You could try using IO#popen The docs are at
class IO - RDoc Documentation

Hope this is what your after.

On 9/20/06, Caio C. [email protected] wrote:

Looks like it. Thanks a lot.

Do you know if it places the exit code in $? ?

I wonder what happens if the subprocess is killed by the system?

I guess I’ll run some tests, but if you happen to know the answers,
I’d be glad to hear them nonetheless.

I haven’t had a lot of experience with it personally. I came across it
in a
plugin I was using that piped to an external progam and had a bit of a
look. The plugin doesn’t seem to do anything special with it and I
havn’t
had a need to use it otherwise.

Sorri I can’t help further.

Caio C. wrote:

Yes?

No, from within a ruby program I want to call some shell command to
act on data stored in a ruby string, so, in the ruby program,
something like, in pseudoruby:

output = system(‘shell_cmd --params etc’, :input => a_string)

Okay, try this:


#!/usr/bin/ruby -w

s = “avast arlington always”

output = echo #{s} | perl -pe "s/a/x/g"

p output


Output:

“xvxst xrlington xlwxys\n”

I want to emphasize there are a dozen ways to do this. This one happens
to
be simple to understand. Another way is File.popen(), but that approach
makes bidirectional communications difficult.

Caio C. wrote:

Looks like it. Thanks a lot.

Do you know if it places the exit code in $? ?

I wonder what happens if the subprocess is killed by the system?

I guess I’ll run some tests, but if you happen to know the answers,
I’d be glad to hear them nonetheless.

The problem with File.popen() is that you want bidirectional
communication
with the child process, and AFAIK that doesn’t offer it – you can
either
read, or write, to the pipe, but not both.

On 2006-09-19, at 22:05 , Paul L. wrote:

output = echo #{s} | perl -pe "s/a/x/g"

I want to emphasize there are a dozen ways to do this. This one
happens to
be simple to understand. Another way is File.popen(), but that
approach
makes bidirectional communications difficult.

I thought about doing it with echo but then I’d have to escape the
string, and what if I was dealing with binary data, and all that…
so popen looks like a more robust solution.

On 9/20/06, Caio C. [email protected] wrote:

to using tempfiles then.
Again, I havn’t used it before, but the IO#pipe command seems to allow
for
bi-directional control.

http://www.ruby-doc.org/core/classes/IO.html#M001252

On 2006-09-19, at 22:10 , Paul L. wrote:

The problem with File.popen() is that you want bidirectional
communication
with the child process, and AFAIK that doesn’t offer it – you can
either
read, or write, to the pipe, but not both.

Hmm, that’s not cool. I’ll run some tests, but looks like it’s back
to using tempfiles then.

Caio C. wrote:

On 2006-09-19, at 22:10 , Paul L. wrote:

The problem with File.popen() is that you want bidirectional
communication
with the child process, and AFAIK that doesn’t offer it – you can
either
read, or write, to the pipe, but not both.

Hmm, that’s not cool. I’ll run some tests, but looks like it’s back
to using tempfiles then.

Did you read my other post? Piping information to child processes is
easy.

bigstring = (lots of data)

output = echo "#{bigstring}" | child_program_name

This has every advantage over using temp files.

On Wed, 20 Sep 2006, Caio C. wrote:

No, from within a ruby program I want to call some shell command to act on
data stored in a ruby string, so, in the ruby program, something like, in
pseudoruby:

output = system(‘shell_cmd --params etc’, :input => a_string)

harp:~ > cat a.rb
require ‘open4’ # gem install open4
require ‘yaml’

stdin = ‘foobar’
stdout = ‘’
stderr = ‘’

Open4.spawn ‘cat’, :stdin => stdin, :stdout => stdout, :stderr => stderr

y ‘stdout’ => stdout
y ‘stderr’ => stderr

harp:~ > ruby a.rb

stdout: foobar

stderr: ‘’

-a

On 2006-09-19, at 22:24 , Daniel N wrote:

Again, I havn’t used it before, but the IO#pipe command seems to
allow for
bi-directional control.

Yea, I figured out how to open a pipe for both read and write. You
use a numeric mode. Here’s what I got:

#!/usr/bin/env ruby
io = File.popen(‘perl -pe s/a/b/g’, 0666)
io.puts(‘abc’)
io.close_write
puts io.read

outputs:
bbc

So it’s fine for my needs. I just need to wrap it into something more
idiomatic.

Thanks Paul and Daniel

On Wed, 20 Sep 2006, Caio C. wrote:

#!/usr/bin/env ruby

Thanks Paul and Daniel

you’re making it pretty hard on yourself:

 harp:~ > cat a.rb
 cat = IO.popen 'cat', 'r+'
 cat.sync = true

 cat.puts 'foobar'
 puts cat.gets

 cat.puts 'barfoo'
 puts cat.gets



 harp:~ > ruby a.rb
 foobar
 barfoo

my session and open4 lib both abstract this largely, including a good
deal of
error handling. there is also the built-in open3 module. check out the
docs.

regards.

-a

On 2006-09-19, at 23:17 , [email protected] wrote:

you’re making it pretty hard on yourself:

I guess I was :slight_smile:

my session and open4 lib both abstract this largely, including a
good deal of
error handling. there is also the built-in open3 module. check
out the docs.

Thanks.

Having a separate stdout and stderr is great, in fact, just the next
thing I was going to ask about. So both open3 and open4 seem like
great candidates.

Could you take some time to sell open4 over open3?

The main advantage I see is the greater abstraction, i.e. the spawn
method.

Caio C. wrote:

I thought about doing it with echo but then I’d have to escape the
string, and what if I was dealing with binary data, and all that…
so popen looks like a more robust solution.

Yes, except for the fact that you cannot easily find out what the child
process created as a result. But yes, if binary data is an issue,
File.popen() will reliably stream the data for you.

On Wed, 20 Sep 2006, Caio C. wrote:

docs.

Thanks.

Having a separate stdout and stderr is great, in fact, just the next thing I
was going to ask about. So both open3 and open4 seem like great candidates.

Could you take some time to sell open4 over open3?

The main advantage I see is the greater abstraction, i.e. the spawn method.

  • you get the child pid returned also with open4

  • a command that fails to exec raises and error (note this is sent back
    from
    the child fork)

  • when using the spawn method a great deal of abstraction is provided,
    not
    least of which that stdin, stdout, stderr are each handled via a
    thread so
    you may provide objects that can handle async output

    Open4.spawn cmd, 1=>stdout_handler 2=>stderr_handler

    and it will be passed to the hanlder in an async way

the main reason, however, is having the pid - it’s hard to kill a proces
without it! :wink:

that said - if all you require is stdout/stderr stay with the stdlib.

regards.

-a

Is Open4 not win32 compatable? With all win32ole hooks, I’m suprised
no one has tackled this yet.

Ara, you helped me a while back with spawning processes in win32,
catching pids and error levels.

–just assumed if I could install it as a gem, it would work.

Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\d0t1q>gem install open4
Attempting local installation of ‘open4’
Local gem file not found: open4*.gem
Attempting remote installation of ‘open4’
Updating Gem source index for: http://gems.rubyforge.org
Successfully installed open4-0.7.0

C:\Documents and Settings\d0t1q>irb
irb(main):001:0> require ‘open4’
=> false
irb(main):002:0> Open4.spawn “echo”, :stdin => “hello”
NameError: uninitialized constant Fcntl::F_SETFD
from
c:/ruby/lib/ruby/gems/1.8/gems/open4-0.7.0/lib/open4.rb:19:in pope n4' from c:/ruby/lib/ruby/gems/1.8/gems/open4-0.7.0/lib/open4.rb:263:in spa
wn’
from c:/ruby/lib/ruby/1.8/timeout.rb:48:in timeout' from c:/ruby/lib/ruby/gems/1.8/gems/open4-0.7.0/lib/open4.rb:262:in spa
wn’
from (irb):2
irb(main):003:0>

On Wed, 20 Sep 2006, x1 wrote:

Is Open4 not win32 compatable? With all win32ole hooks, I’m suprised
no one has tackled this yet.

Ara, you helped me a while back with spawning processes in win32,
catching pids and error levels.

–just assumed if I could install it as a gem, it would work.

ah. no - it is not.

popen, or something out of the win32 project, it is then.

there is also this

http://popen4.rubyforge.org/

which i haven’t tried.

regards.

-a