Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

splitting command-line arguments

by IraTarball (Monk)
on May 26, 2001 at 05:19 UTC ( [id://83460]=perlquestion: print w/replies, xml ) Need Help??

IraTarball has asked for the wisdom of the Perl Monks concerning the following question:

I may just be frain bried but...

I have a script that is meant to manage a number of other executables and I am trying to get an array of the arguments. So a command like:

getStuff configs --force=all --out out.txt -v -a
should end up with an args array equivalent to
@args = ( "configs", "--force=all", "--out out.txt", "-v", "-a");
I can do it with a split /(-+)/ and  for ($i = 0; $i < $#args; $i++) type of thing. I look for -- and piece the pairs back together. But who wants to do that? Not only do I suspect there's another way to do it, I'll bet there's a better way to do it.
Any suggestions?

Thanks for your help! Ira

"So... What do all these little arrows mean?"
~unknown

Replies are listed 'Best First'.
Re: join this
by btrott (Parson) on May 26, 2001 at 07:39 UTC
    I don't think you're specifying the problem well enough. The line you give as an example of an argument list:
    getStuff configs --force=all --out out.txt -v -a
    will *not* necessarily be parsed into a list like the one you posted. It is *entirely* up to the program who is receiving these arguments how to parse them, and what to do with them.

    As an example, you are implying that the "--out" option should be followed by a string argument (this is implied by the fact that in your list, you have the line "--out out.txt". But how can you possibly know that, given an arbitrary set of options? "--out" could simply be a boolean flag, not taking any additional arguments, and "out.txt" could be a file supplied as an argument to the program that you're running.

    The point is, your post suggest no way of discerning this information, and w/o that, there's really no way we can help you parse the config options. And in fact, although I don't know what your script is trying to do, I would strongly discourage you from trying to parse the command line options to another program--unless you *are* that other program, you have no way of knowing what the options mean, and what arguments they take, etc.

    Now, if you *do* know that information, then you can use Getopt::Long to parse the arguments, kind of like this:

    use Getopt::Long; use Data::Dumper; { local @ARGV = ("configs", "--force=all", "--out", "out.txt", " +-v", "-a"); GetOptions(\my %opts, "force=s", "out=s", "v", "a"); print Dumper \%opts; print Dumper \@ARGV; }
    This gives:
    $VAR1 = { 'out' => 'out.txt', 'a' => 1, 'v' => 1, 'force' => 'all' }; $VAR1 = [ 'configs' ];
    But just as a demonstration of what I was saying above, if we change that GetOptions spec to this:
    GetOptions(\my %opts, "force=s", "out", "v", "a");
    we now get this:
    $VAR1 = { 'out' => 1, 'a' => 1, 'v' => 1, 'force' => 'all' }; $VAR1 = [ 'configs', 'out.txt' ];
    Which just goes to show you that unless you know what, for example, "--out" means, you're really out of luck.
Re: join this
by myocom (Deacon) on May 26, 2001 at 05:22 UTC

    What you want is GetOpt::Long. It will handle all your command line option needs.

Re: join this
by TStanley (Canon) on May 26, 2001 at 05:24 UTC
    The Getopt::Long or Getopt::Std modules, which are part of the Perl distribution, handle the passing of command line arguments. As I recall, one of the options allows you to put those arguments into a hash.

    TStanley
    --------
    There's an infinite number of monkeys outside who want to talk to us
    about this script for Hamlet they've worked out
    -- Douglas Adams/Hitchhiker's Guide to the Galaxy
      I'm sorry, but I guess I wasn't clear about the situation. The arguments I'm trying to parse into the array are not arguments to my script, they're arguments to what will become a child process. So my script is getting them from a file that contains entries like the one in my original post, I can easily parse out the command name, but I'm having trouble getting the argument list into a form that I could pass to, say system() for example.

      Thanks for the quick responses.
      Ira.

      "So... What do all these little arrows mean?" ~unknown

        Do you know the format of the command lines that you will be getting from the file? If so, you can still use Getopt::Long or Getopt::Std if you localize @ARGV. Here's a bit of code I've used in the past to accomplish a similar task. This code uses Getopt::Std and the parse_cmd function returns a hash rather than an array so it doesn't meet IraTarball's exact specs, but it should be a nudge in the right direction. Here goes --

        use strict; use Getopt::Std; use Data::Dumper; sub parse_cmd{ my $cmdline = shift; local @ARGV; @ARGV = split('\s+',$cmdline); shift @ARGV; my %options; getopts("x:y:z:abc", \%options); return %options; } my %opts = parse_cmd("someprg -x foo -y bar -z baz -ab"); print Dumper(\%opts);
        Output from perl test.pl -f commandline.txt:

        $VAR1 = { 'x' => 'foo', 'y' => 'bar', 'a' => 1, 'z' => 'baz', 'b' => 1 };

        The logic would be the same for Getopt::Long or any of the other command line parsing options on cpan. Of course if you don't know what possible command line arguments are going to be passed, then you may want either look at something like Parse::RecDecent or look at the code of Getopt::Long.

        ----
        Coyote

Re: join this
by converter (Priest) on May 26, 2001 at 18:31 UTC
    A slight change to the format of your input file would make your job a lot easier:

    getStuff
    configs
    --force=all
    --out out.txt
    -v
    -a
    
    getOtherStuff
    configs
    --force=all
    --out out2.txt
    -v
    -a
    

    By separating each argument with a newline, you remove the need to write code to guess how the target program parses its arguments. You may now use <>'s paragraph mode to parse the arguments into an array:

    $/ = ""; $" = ", "; while (<FH>) { my @args = split /\n/, $_; print "args: @args\n"; }

    prints:

    args: getStuff, configs, --force=all, --out out.txt, -v, -a
    args: getOtherStuff, configs, --force=all, --out out2.txt, -v
    

Re: join this
by IraTarball (Monk) on May 30, 2001 at 01:32 UTC
    I really appreciate everyone's help here but I'm clearly not being clear. That's my fault and I've already punnished my wicked flesh for wasting everyone's brain cells. Let me try being a bit more explicit...

    I'm writing a cron daemon in Perl.

    O.K. not really, but sort of. At this point most people should be ready to ask "Why would you want to do something like that?" and the answer is basically, they want to pay me for it (could be worse, they could want me to encode everything Encoding Script).

    So, this cron daemon does everything serially so I don't need to worry about any time specifiers, I just have the programs to run in a text file (crontab sort of thing). An entry might look like.

    "getStuff" configs --force=all --out out.txt -v -a
    I'm going to want to pass this to system() at some point and I'm having a problem just passing in the complete line. I keep getting
    'D:\Documents' is not recognized as an internal or external command, operable program or batch file.
    Yes. Windows. I've punnished my flesh for that too.

    Here is a simple example of what I want.

    while (<DATA>) { chomp; s/#.*$//; #Clear out comments next unless $_ && $_ ne ''; m/^\s+\Q"\E(.+?)\Q"\E\s*(.*$)/; $task = $1; @args = split /\s+/, $2 if $2; for (my $i = 0; $i < $#args; $i++) { #Arguments begin with - or -- or + if ($args[$i] =~ /^[-\+]/) { #if this looks like an argument, and the next one # doesn't, prepend the next thing to this one. $args[$i] .= " " . $args[$i+1] if $args[$i+1] =~ /^[^-\+]/; #remove the thing from the args array. splice @args, $i+1, 1; } } system ($task, @args); }
    This seems to work. Well the thing I snipped it from seems to work but I might have screwed it up pasting it in here.
    I keep thinking that there is probably a better way of doing this, but I can't seem to come up with one.

    If anyone has any suggestions...
    Thanks,
    Ira

    "We demand rigidly defined areas of doubt and uncertainty!"
    ~Vroomfondel (Hitch Hickers Guide to the Galaxy)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://83460]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (5)
As of 2024-04-26 08:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found