2

有没有一种好的(理想情况下是 CPAN)方法来使用 Perl处理任意命令行选项?

例如,获取一个字符串"-a 1 -b 'z -x' -c -d 3 4"并生成一个GetOpt::Long类似的数据结构:

 { a=>1, b=>"z -x", c=>1, d=>[3,4] }  # d=>"3 4" is acceptable

需要注意的是

  1. 选项集是事先不知道的;所以我们似乎不能GetOpt::Long按原样使用。

  2. 值本身可以包含其他选项,因此简单地解析字符串#\b+-(\S+)\b#以查找该字符串中的所有选项似乎也是不可能的,并且更复杂的是一些参数是 =s 类型,一些 =s@,一些 " -xab c" 类型。

  3. 此外,即使我们可以做#2,是否GetOptionsFromString支持尊重引用值的正确标记?


注意:为了练习的目的,假设所有参数都是“选项”,换句话说,如果您将字符串拆分为(可能带引号的)标记,您的结构总是

 "-opt1 arg1a [arg1b] -opt2 ....".

换句话说,任何以破折号开头的单词/标记都是一个新选项,所有后续不以破折号开头的单词/标记都是该选项的值。

4

2 回答 2

4

使用 Text::Parsewords 和简单状态机的快速示例。

#!/usr/bin/env perl

use strict;
use warnings;

use Text::ParseWords qw/shellwords/;

my $str = q{-a 1 -b 'z -x' -c -d 3 4};
my $data = parse($str);

use Data::Printer;
p $data;

sub parse {
  my $str = shift;

  my @tokens = shellwords $str;

  my %data;
  my @keys;
  my $key = '_unknown';
  foreach my $token (@tokens) {
    if ($token =~ s/^\-//) {
      $key = $token;
      push @keys, $key;
      next;
    }

    if ( ref $data{$key} ) {
      push @{ $data{$key} }, $token;
    } elsif (defined $data{$key}) {
      $data{$key} = [ $data{$key}, $token ];
    } else {
      $data{$key} = $token;
    }
  }

  foreach my $key (@keys) {
    next if defined $data{$key};
    $data{$key} = 1;
  }

  return \%data;
}
于 2012-11-28T20:47:07.677 回答
1

在我知道Getopt::Long并使用它的智慧之前,我推出了我自己的命令行选项处理器,它可以接受任意参数并填充一个全局哈希表。规则是

使用单个字母切换(-A .. -Z、-a .. -z)

-n               sets  $args{"n"} = 1
-nfoo            sets  $args{"n"} = "foo"

多于一个字母的开关

--foo            sets  $args{"foo"} = 1
--foo=bar        sets  $args{"foo"} = "bar"

这种方法的好处和坏处在于,您可以快速尝试新的命令行选项,在使用该选项时更改代码,而无需编辑调用GetOptions或分配另一个变量:

 ... line 980 ...
 if ($args{"do-experimental-thing"}) {
     # new code
     do_experimental_thing();
 } else {
     do_normal_thing();
 }

这是我上传到 CPAN 的第一个模块,后来我删除了它,但BackPAN的记忆力很长。

于 2012-11-28T20:46:34.150 回答