Calling a subprocess with specific arguments and capturing itsoutput?

D

Dan Q

Hi. I haven't written Ruby in a while, and I was wondering if someone
could help with a problem I've never managed to solve. I'm writing a
shell script that takes filenames as its arguments, and calls "du" to
get their size. Because I don't want to have to escape everything
perfectly, I was looking for a function with a syntax that allows
separate arguments, like system("du", "-sh", filename).

system() doesn't allow me to capture the output of the subprocess,
while popen , ``, and %x() don't let me specify a list of discrete
arguments. Is there an elegant solution to this problem? My current
solution is to just escape the arguments and put them into a string,
but this is ugly and buggy.

Thanks in advance!
-Dan
 
G

ghorner

Hi. I haven't written Ruby in a while, and I was wondering if someone
could help with a problem I've never managed to solve. I'm writing a
shell script that takes filenames as its arguments, and calls "du" to
get their size. Because I don't want to have to escape everything
perfectly, I was looking for a function with a syntax that allows
separate arguments, like system("du", "-sh", filename).

def backtick(cmd,*args)
IO.popen('-') {|f| f ? f.read : exec(cmd,*args)}
end

backtick 'du', '-sh', filename
 
B

Bill Kelly

From: "Dan Q said:
Hi. I haven't written Ruby in a while, and I was wondering if someone
could help with a problem I've never managed to solve. I'm writing a
shell script that takes filenames as its arguments, and calls "du" to
get their size. Because I don't want to have to escape everything
perfectly, I was looking for a function with a syntax that allows
separate arguments, like system("du", "-sh", filename).

system() doesn't allow me to capture the output of the subprocess,
while popen , ``, and %x() don't let me specify a list of discrete
arguments. Is there an elegant solution to this problem? My current
solution is to just escape the arguments and put them into a string,
but this is ugly and buggy.

One possibility might be to let Shellwords handle the
escaping:

irb(main):001:0> require 'shellwords'
irb(main):002:0> args = ["foo", "bar baz", "mary 'had' a", 'little "lamb" and stuff']
irb(main):003:0> puts( args.map {|a| a.shellescape}.join(" ") )
foo bar\ baz mary\ \'had\'\ a little\ \"lamb\"\ and\ stuff


Alternately, if you can use ruby 1.9.x, there appears to be
a very powerful new Kernel.spawn command. (See the `ri`
documentation, several pages...)

Also in 1.9.x, it appears to now be possible to pass an
Array for the command arguments to popen:

irb(main):001:0> IO.popen(["ls", "-l", "GL_GameOfLife.zip", "quake3-1.32b-source.zip"], "r") {|io| puts(io.read)}
-rw------- 1 billk billk 3540592 Feb 23 2009 GL_GameOfLife.zip
-rw------- 1 billk billk 5724791 Sep 21 2005 quake3-1.32b-source.zip


Hope this helps,

Bill
 
D

Dan Q

def backtick(cmd,*args)
=A0IO.popen('-') {|f| f ? f.read : exec(cmd,*args)}
end

backtick 'du', '-sh', filename

Thanks! That's a very nice solution, and more concise than I would
have thought possible. I'll use this until ruby-1.9 or 2.0 becomes
standard.

-Dan
 
D

Dan Q

Alternately, if you can use ruby 1.9.x, there appears to be
a very powerful new Kernel.spawn command. =A0(See the `ri`
documentation, several pages...)

Also in 1.9.x, it appears to now be possible to pass an Array for the
command arguments to popen:

irb(main):001:0> IO.popen(["ls", "-l", "GL_GameOfLife.zip",
"quake3-1.32b-source.zip"], "r") {|io| puts(io.read)}

Thanks, Bill. I'm glad to hear that the standard library will be
getting the capability to do this without a hack, because it seems
like a big omission in ruby-1.8.

-Dan
 
T

Tony Arcieri

[Note: parts of this message were removed to make it a legal post.]

I was looking for a function with a syntax that allows
separate arguments, like system("du", "-sh", filename)

Why is this any harder than?

`#{cmd} #{args.map { |arg| arg.to_s.inspect }.join(' ')}`
 
D

Dan Q

Why is this any harder than?

`#{cmd} #{args.map { |arg| arg.to_s.inspect }.join(' ')}`

That comes a lot closer to working than I had expected ;)
I'd rather not deal with escaping, at all. Won't shell commands in
backticks be run differently, depending on what the system shell is?
I'm not sure whether all unix shells have similar escaping syntax.

The escaping done by inspect() seems to fail when the filename begins
with whitespace or contains a newline.

-Dan
 
R

Robert Klemme

2009/11/11 Dan Q said:
That comes a lot closer to working than I had expected ;)
I'd rather not deal with escaping, at all. Won't shell commands in
backticks be run differently, depending on what the system shell is?
I'm not sure whether all unix shells have similar escaping syntax.

The escaping done by inspect() seems to fail when the filename begins
with whitespace or contains a newline.

If you are on 1.9, escaping is superfluous: you can pass an Array of
arguments directly:

irb(main):001:0> IO.popen(%w{echo *}, "r") {|io| puts io.read}
*
=> nil
irb(main):002:0> IO.popen(["ls", "-a", "-F"], "r") {|io| puts io.read}
/
../
=> nil

This is a lot safer, significantly less error prone and is probably
more efficient as well (because there is no shell needed for parsing
the command line).

As far as I can see this does not work in 1.8.7.

Kind regards

robert
 
J

James Edward Gray II

Won't shell commands in backticks be run differently, depending on =
what the system shell is? I'm not sure whether all unix shells have =
similar escaping syntax.

I don't believe so. I think backticks will default to the lowest common =
denominator of sh.

James Edward Gray II=
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top