Calling a subprocess with specific arguments and capturing itsoutput?

Discussion in 'Ruby' started by Dan Q, Nov 10, 2009.

  1. Dan Q

    Dan Q Guest

    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
    Dan Q, Nov 10, 2009
    #1
    1. Advertising

  2. Dan Q

    ghorner Guest

    On Nov 10, 6:00 pm, Dan Q <> wrote:
    > 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
    ghorner, Nov 10, 2009
    #2
    1. Advertising

  3. Dan Q

    Bill Kelly Guest

    From: "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.


    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
    Bill Kelly, Nov 10, 2009
    #3
  4. Dan Q

    Dan Q Guest

    Re: Calling a subprocess with specific arguments and capturing its output?

    > 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
    Dan Q, Nov 10, 2009
    #4
  5. Dan Q

    Dan Q Guest

    On Tue, Nov 10, 2009 at 5:43 PM, Bill Kelly <> wrote:
    > 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
    Dan Q, Nov 10, 2009
    #5
  6. Dan Q

    Tony Arcieri Guest

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

    On Tue, Nov 10, 2009 at 4:00 PM, Dan Q <> wrote:

    > 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(' ')}`

    --
    Tony Arcieri
    Medioh/Nagravision
    Tony Arcieri, Nov 11, 2009
    #6
  7. Dan Q

    Dan Q Guest

    On Tue, Nov 10, 2009 at 9:48 PM, Tony Arcieri <> wrote:
    > On Tue, Nov 10, 2009 at 4:00 PM, Dan Q <> wrote:
    >
    >> 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(' ')}`


    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
    Dan Q, Nov 11, 2009
    #7
  8. 2009/11/11 Dan Q <>:
    > On Tue, Nov 10, 2009 at 9:48 PM, Tony Arcieri <> wrote:
    >> On Tue, Nov 10, 2009 at 4:00 PM, Dan Q <> wrote:
    >>
    >>> 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(' ')}`

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


    --
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
    Robert Klemme, Nov 11, 2009
    #8
  9. On Nov 11, 2009, at 2:45 AM, Dan Q wrote:

    > 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=
    James Edward Gray II, Nov 11, 2009
    #9
    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. Surendra
    Replies:
    0
    Views:
    491
    Surendra
    Mar 23, 2006
  2. Fuzzyman
    Replies:
    3
    Views:
    616
    Gabriel Genellina
    Jan 7, 2007
  3. Eric
    Replies:
    3
    Views:
    606
  4. hiral
    Replies:
    2
    Views:
    574
    Jean-Michel Pichavant
    May 5, 2010
  5. Navaneeth
    Replies:
    4
    Views:
    536
    Kenny McCormack
    Nov 20, 2010
Loading...

Share This Page