# Good library for command pipes in Ruby?

**URL:** https://rubytalk.org/t/good-library-for-command-pipes-in-ruby/74306
**Category:** ruby-talk
**Created:** [17 October 2017 22:40 UTC](https://rubytalk.org/t/good-library-for-command-pipes-in-ruby/74306 "2017-10-17T22:40:25Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![Samuel\_Williams](https://avatars.discourse-cdn.com/v4/letter/s/db5fbb/32.png) [@Samuel\_Williams](https://rubytalk.org/u/Samuel_Williams)
#### Post date: [17 October 2017 22:40 UTC](https://rubytalk.org/t/good-library-for-command-pipes-in-ruby/74306/1 "2017-10-17T22:40:25Z")

</div>

Hello,

I tried looking for a library that lets me write (lazy) command line pipes  
in Ruby but came up short.

I would like to make a function that invokes a executable to transform an  
object into text and either saves it to a file or dumps it out.

Ideally, I could write it like this:

&nbsp;&nbsp;def pipeline  
&nbsp;&nbsp;&nbsp;&nbsp;return self.to\_text \>\> Process.spawn("dot")  
&nbsp;&nbsp;end

Ideally, this pipeline is not invoked until the user does the following:

&nbsp;&nbsp;&nbsp;&nbsp;pipeline.write(path) //writes to file

or

&nbsp;&nbsp;&nbsp;&nbsp;pipeline.read //returns String

Does something like this exist already?

Thanks  
Samuel

---

<div class="post-metadata">

### Author: ![Eric\_Wong](https://avatars.discourse-cdn.com/v4/letter/e/a3d4f5/32.png) [@Eric\_Wong](https://rubytalk.org/u/Eric_Wong)
#### Post date: [17 October 2017 23:06 UTC](https://rubytalk.org/t/good-library-for-command-pipes-in-ruby/74306/2 "2017-10-17T23:06:06Z")

</div>

> Hello,
> 
> I tried looking for a library that lets me write (lazy) command line pipes  
> in Ruby but came up short.
> 
> I would like to make a function that invokes a executable to transform an  
> object into text and either saves it to a file or dumps it out.

Maybe using arrays to build up the command is enough;  
at least that's all I use.

> Ideally, I could write it like this:
> 
> &nbsp;&nbsp;def pipeline  
> &nbsp;&nbsp;&nbsp;&nbsp;return self.to\_text \>\> Process.spawn("dot")  
> &nbsp;&nbsp;end
> 
> Ideally, this pipeline is not invoked until the user does the following:
> 
> &nbsp;&nbsp;&nbsp;&nbsp;pipeline.write(path) //writes to file

&nbsp;&nbsp;command = # ... build the command array...

Then maybe something like:

&nbsp;&nbsp;IO.pipe do |r, w|  
&nbsp;&nbsp;&nbsp;&nbsp;th = Thread.new { w.write(self.to\_text) }  
&nbsp;&nbsp;&nbsp;&nbsp;pid = Process.spawn(env, \*command, in: r, out: path)  
&nbsp;&nbsp;&nbsp;&nbsp;th.join  
&nbsp;&nbsp;&nbsp;&nbsp;Process.waitpid2(pid)  
&nbsp;&nbsp;end

[\*] of course, I'd like to get Thriber accepted to make the above  
&nbsp;&nbsp;&nbsp;&nbsp;cheaper with s/Thread/Thriber/: [https://bugs.ruby-lang.org/issues/13618](https://bugs.ruby-lang.org/issues/13618)  
&nbsp;&nbsp;&nbsp;&nbsp;Too late for 2.5, I guess :\<

> or
> 
> &nbsp;&nbsp;&nbsp;&nbsp;pipeline.read //returns String

IO.popen(..., &:read) and `backtick` work well for me with tiny  
command outputs; I prefer IO.popen with arrays to avoid  
escaping.

But one of the best things about pipes (and IO objects in  
general) is doing incremental processing for large outputs or  
inputs. RAM is precious to me and I frequently work with  
large files, so I prefer to use IO#readpartial or similar.

Unbound IO#read and #gets gives me nightmares :\<

> Does something like this exist already?

I started working along building complex pipelines as arrays  
(and also throwing Procs anywhere in the pipeline) at:

&nbsp;&nbsp;[http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/435624](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/435624)

And eventually for dtas \<[https://80x24.org/dtas/&gt;:](https://80x24.org/dtas/&gt;:)

&nbsp;&nbsp;[https://80x24.org/dtas-all/20170428200809.28822-1-e@80x24.org/raw](https://80x24.org/dtas-all/20170428200809.28822-1-e@80x24.org/raw)

But haven't really gotten around to doing more along those lines.

> **···**
>
> Samuel Williams \<space.ship.traveller@gmail.com\> wrote:

---

<div class="post-metadata">

### Author: ![Samuel\_Williams](https://avatars.discourse-cdn.com/v4/letter/s/db5fbb/32.png) [@Samuel\_Williams](https://rubytalk.org/u/Samuel_Williams)
#### Post date: [18 October 2017 01:16 UTC](https://rubytalk.org/t/good-library-for-command-pipes-in-ruby/74306/3 "2017-10-18T01:16:54Z")

</div>

Interesting.. I started hacking something together, it's pretty small, but  
it looks like this:

&nbsp;&nbsp;&nbsp;&nbsp;pipeline = Process::Pipeline.("cat Rakefile").("strings").("sort")  
&nbsp;&nbsp;pipeline \> "output.txt"

Or...

&nbsp;&nbsp;&nbsp;&nbsp;buffer = pipeline.read

It composes together immutable steps, so you can compose pipes together,  
e.g.

&nbsp;&nbsp;&nbsp;&nbsp;pipeline2 = pipeline.("tee foo.txt")

the original pipeline is shared but not modified in any way.

What do you think?

> **···**
>
> On 18 October 2017 at 12:06, Eric Wong \<e@80x24.org\> wrote:
> 
> > Samuel Williams \<space.ship.traveller@gmail.com\> wrote:  
> > \> Hello,  
> > \>  
> > \> I tried looking for a library that lets me write (lazy) command line  
> > pipes  
> > \> in Ruby but came up short.  
> > \>  
> > \> I would like to make a function that invokes a executable to transform an  
> > \> object into text and either saves it to a file or dumps it out.
> > 
> > Maybe using arrays to build up the command is enough;  
> > at least that's all I use.
> > 
> > \> Ideally, I could write it like this:  
> > \>  
> > \> def pipeline  
> > \> return self.to\_text \>\> Process.spawn("dot")  
> > \> end  
> > \>  
> > \> Ideally, this pipeline is not invoked until the user does the following:  
> > \>  
> > \> pipeline.write(path) //writes to file
> > 
> > &nbsp;&nbsp;command = # ... build the command array...
> > 
> > Then maybe something like:
> > 
> > &nbsp;&nbsp;IO.pipe do |r, w|  
> > &nbsp;&nbsp;&nbsp;&nbsp;th = Thread.new { w.write(self.to\_text) }  
> > &nbsp;&nbsp;&nbsp;&nbsp;pid = Process.spawn(env, \*command, in: r, out: path)  
> > &nbsp;&nbsp;&nbsp;&nbsp;th.join  
> > &nbsp;&nbsp;&nbsp;&nbsp;Process.waitpid2(pid)  
> > &nbsp;&nbsp;end
> > 
> > [\*] of course, I'd like to get Thriber accepted to make the above  
> > &nbsp;&nbsp;&nbsp;&nbsp;cheaper with s/Thread/Thriber/: [https://bugs.ruby-lang.org/](https://bugs.ruby-lang.org/)  
> > issues/13618  
> > &nbsp;&nbsp;&nbsp;&nbsp;Too late for 2.5, I guess :\<
> > 
> > \> or  
> > \>  
> > \> pipeline.read //returns String
> > 
> > IO.popen(..., &:read) and `backtick` work well for me with tiny  
> > command outputs; I prefer IO.popen with arrays to avoid  
> > escaping.
> > 
> > But one of the best things about pipes (and IO objects in  
> > general) is doing incremental processing for large outputs or  
> > inputs. RAM is precious to me and I frequently work with  
> > large files, so I prefer to use IO#readpartial or similar.
> > 
> > Unbound IO#read and #gets gives me nightmares :\<
> > 
> > \> Does something like this exist already?
> > 
> > I started working along building complex pipelines as arrays  
> > (and also throwing Procs anywhere in the pipeline) at:
> > 
> > &nbsp;&nbsp;[http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/435624](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/435624)
> > 
> > And eventually for dtas \<[https://80x24.org/dtas/&gt;:](https://80x24.org/dtas/&gt;:)
> > 
> > &nbsp;&nbsp;[https://80x24.org/dtas-all/20170428200809.28822-1-e@80x24.org/raw](https://80x24.org/dtas-all/20170428200809.28822-1-e@80x24.org/raw)
> > 
> > But haven't really gotten around to doing more along those lines.
> > 
> > Unsubscribe: \<mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe\>  
> > \<[http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt](http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt);

---

<div class="post-metadata">

### Author: ![Eric\_Wong](https://avatars.discourse-cdn.com/v4/letter/e/a3d4f5/32.png) [@Eric\_Wong](https://rubytalk.org/u/Eric_Wong)
#### Post date: [18 October 2017 02:21 UTC](https://rubytalk.org/t/good-library-for-command-pipes-in-ruby/74306/4 "2017-10-18T02:21:23Z")

</div>

> Interesting.. I started hacking something together, it's pretty small, but  
> it looks like this:
> 
> &nbsp;&nbsp;&nbsp;&nbsp;pipeline = Process::Pipeline.("cat Rakefile").("strings").("sort")  
> &nbsp;&nbsp;pipeline \> "output.txt"

Maybe only support arrays (instead of string commands) to avoid  
shell injection problems. Ruby is especially great (compared to Perl5)  
since we even have %W for expressing arrays with variables.

> Or...
> 
> &nbsp;&nbsp;&nbsp;&nbsp;buffer = pipeline.read

Streaming support is most important:

&nbsp;&nbsp;pipeline.each\_chunk(size: 16384) { |buf| ... }  
&nbsp;&nbsp;pipeline.each\_line(limit: 4096) { |line| ... }

> It composes together immutable steps, so you can compose pipes together,  
> e.g.
> 
> &nbsp;&nbsp;&nbsp;&nbsp;pipeline2 = pipeline.("tee foo.txt")
> 
> the original pipeline is shared but not modified in any way.
> 
> What do you think?

\*shrug\* I think I'd still rather be manipulating arrays, since  
it's less new stuff to learn API-wise. And punctuation-oriented  
APIs make searching harder, but maybe that's just me...

> **···**
>
> Samuel Williams \<space.ship.traveller@gmail.com\> wrote:

---

<div class="post-metadata">

### Author: ![Samuel\_Williams](https://avatars.discourse-cdn.com/v4/letter/s/db5fbb/32.png) [@Samuel\_Williams](https://rubytalk.org/u/Samuel_Williams)
#### Post date: [18 October 2017 02:40 UTC](https://rubytalk.org/t/good-library-for-command-pipes-in-ruby/74306/5 "2017-10-18T02:40:10Z")

</div>

So, the commands are passed directly to `Process.spawn` so if you want to  
write your command like ('cat', arg) it would work - the full args could be  
([env,] \*command, \*\*options) off the top of my head.

It should be possible to implement each\_line and each\_chunk - I think these  
are good ideas.

> **···**
>
> On 18 October 2017 at 15:21, Eric Wong \<e@80x24.org\> wrote:
> 
> > Samuel Williams \<space.ship.traveller@gmail.com\> wrote:  
> > \> Interesting.. I started hacking something together, it's pretty small,  
> > but  
> > \> it looks like this:  
> > \>  
> > \> pipeline = Process::Pipeline.("cat Rakefile").("strings").("sort")  
> > \> pipeline \> "output.txt"
> > 
> > Maybe only support arrays (instead of string commands) to avoid  
> > shell injection problems. Ruby is especially great (compared to Perl5)  
> > since we even have %W for expressing arrays with variables.
> > 
> > \> Or...  
> > \>  
> > \> buffer = pipeline.read
> > 
> > Streaming support is most important:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pipeline.each\_chunk(size: 16384) { |buf| ... }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pipeline.each\_line(limit: 4096) { |line| ... }
> > 
> > \> It composes together immutable steps, so you can compose pipes together,  
> > \> e.g.  
> > \>  
> > \> pipeline2 = pipeline.("tee foo.txt")  
> > \>  
> > \> the original pipeline is shared but not modified in any way.  
> > \>  
> > \> What do you think?
> > 
> > \*shrug\* I think I'd still rather be manipulating arrays, since  
> > it's less new stuff to learn API-wise. And punctuation-oriented  
> > APIs make searching harder, but maybe that's just me...
> > 
> > Unsubscribe: \<mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe\>  
> > \<[http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt](http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt);

---

<div class="post-metadata">

### Author: ![Andy\_Jones](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/andy_jones/32/1829_2.png) [@Andy\_Jones](https://rubytalk.org/u/Andy_Jones)
#### Post date: [18 October 2017 07:42 UTC](https://rubytalk.org/t/good-library-for-command-pipes-in-ruby/74306/6 "2017-10-18T07:42:41Z")

</div>

You might want to take a look at [GitHub - piotrmurach/tty-command: Execute shell commands with pretty output logging and capture stdout, stderr and exit status.](https://github.com/piotrmurach/tty-command) -- not sure if it does what you need, but it might be of some help.

> **···**
>
> From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Samuel Williams  
> Sent: 18 October 2017 03:40  
> To: Ruby users  
> Subject: Re: Good library for command pipes in Ruby?
> 
> So, the commands are passed directly to `Process.spawn` so if you want to write your command like ('cat', arg) it would work - the full args could be ([env,] \*command, \*\*options) off the top of my head.
> 
> It should be possible to implement each\_line and each\_chunk - I think these are good ideas.
> 
> On 18 October 2017 at 15:21, Eric Wong \<e@80x24.org\<mailto:e@80x24.org\>\> wrote:  
> Samuel Williams \<space.ship.traveller@gmail.com\<mailto:space.ship.traveller@gmail.com\>\> wrote:
> 
> > Interesting.. I started hacking something together, it's pretty small, but  
> > it looks like this:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;pipeline = Process::Pipeline.("cat Rakefile").("strings").("sort")  
> > &nbsp;&nbsp;pipeline \> "output.txt"
> 
> Maybe only support arrays (instead of string commands) to avoid  
> shell injection problems. Ruby is especially great (compared to Perl5)  
> since we even have %W for expressing arrays with variables.
> 
> > Or...
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;buffer = pipeline.read
> 
> Streaming support is most important:
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pipeline.each\_chunk(size: 16384) { |buf| ... }  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pipeline.each\_line(limit: 4096) { |line| ... }
> 
> > It composes together immutable steps, so you can compose pipes together,  
> > e.g.
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;pipeline2 = pipeline.("tee foo.txt")
> > 
> > the original pipeline is shared but not modified in any way.
> > 
> > What do you think?
> 
> \*shrug\* I think I'd still rather be manipulating arrays, since  
> it's less new stuff to learn API-wise. And punctuation-oriented  
> APIs make searching harder, but maybe that's just me...
> 
> Unsubscribe: \<mailto:ruby-talk-request@ruby-lang.org\<mailto:ruby-talk-request@ruby-lang.org\>?subject=unsubscribe\>  
> \<[http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt](http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt);
> 
> Click here to view Company Information and Confidentiality Notice.\<[http://www.jameshall.co.uk/index.php/small-print/email-disclaimer&gt](http://www.jameshall.co.uk/index.php/small-print/email-disclaimer&gt);

---

<div class="post-metadata">

### Author: ![Samuel\_Williams](https://avatars.discourse-cdn.com/v4/letter/s/db5fbb/32.png) [@Samuel\_Williams](https://rubytalk.org/u/Samuel_Williams)
#### Post date: [18 October 2017 08:47 UTC](https://rubytalk.org/t/good-library-for-command-pipes-in-ruby/74306/7 "2017-10-18T08:47:36Z")

</div>

Cool, I like the TTY series of gems, they are pretty useful for scripts..

Well, I did finally release something  
[GitHub - ioquatix/process-pipeline](https://github.com/ioquatix/process-pipeline) it was just my side project  
for the day, I think it's useful.

Thanks for everyone's ideas 🙂

> **···**
>
> On 18 October 2017 at 20:42, Andy Jones \<Andy.Jones@jameshall.co.uk\> wrote:
> 
> > You might want to take a look at [https://github.com/](https://github.com/)  
> > piotrmurach/tty-command -- not sure if it does what you need, but it  
> > might be of some help.
> > 
> > \*From:\* ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] \*On Behalf Of \*Samuel  
> > Williams  
> > \*Sent:\* 18 October 2017 03:40  
> > \*To:\* Ruby users  
> > \*Subject:\* Re: Good library for command pipes in Ruby?
> > 
> > So, the commands are passed directly to `Process.spawn` so if you want to  
> > write your command like ('cat', arg) it would work - the full args could be  
> > ([env,] \*command, \*\*options) off the top of my head.
> > 
> > It should be possible to implement each\_line and each\_chunk - I think  
> > these are good ideas.
> > 
> > On 18 October 2017 at 15:21, Eric Wong \<e@80x24.org\> wrote:
> > 
> > Samuel Williams \<space.ship.traveller@gmail.com\> wrote:  
> > \> Interesting.. I started hacking something together, it's pretty small,  
> > but  
> > \> it looks like this:  
> > \>  
> > \> pipeline = Process::Pipeline.("cat Rakefile").("strings").("sort")  
> > \> pipeline \> "output.txt"
> > 
> > Maybe only support arrays (instead of string commands) to avoid  
> > shell injection problems. Ruby is especially great (compared to Perl5)  
> > since we even have %W for expressing arrays with variables.
> > 
> > \> Or...  
> > \>  
> > \> buffer = pipeline.read
> > 
> > Streaming support is most important:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pipeline.each\_chunk(size: 16384) { |buf| ... }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pipeline.each\_line(limit: 4096) { |line| ... }
> > 
> > \> It composes together immutable steps, so you can compose pipes together,  
> > \> e.g.  
> > \>  
> > \> pipeline2 = pipeline.("tee foo.txt")  
> > \>  
> > \> the original pipeline is shared but not modified in any way.  
> > \>  
> > \> What do you think?
> > 
> > \*shrug\* I think I'd still rather be manipulating arrays, since  
> > it's less new stuff to learn API-wise. And punctuation-oriented  
> > APIs make searching harder, but maybe that's just me...
> > 
> > Unsubscribe: \<mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe\>  
> > \<[http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt](http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt);
> > 
> > Click here to view Company Information and Confidentiality Notice.  
> > \<[http://www.jameshall.co.uk/index.php/small-print/email-disclaimer&gt](http://www.jameshall.co.uk/index.php/small-print/email-disclaimer&gt);
> > 
> > Unsubscribe: \<mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe\>  
> > \<[http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt](http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt);
