【问题标题】:Is there a CPAN way to parse random command line options?是否有 CPAN 方法来解析随机命令行选项?
【发布时间】:2012-11-16 19:28:45
【问题描述】:

有没有一种好的(理想情况下是 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. 1234563 ,一些=s@,一些“-xabc”类型。
  3. 此外,即使我们可以执行 #2,GetOptionsFromString 是否支持尊重引用值的正确标记?


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

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

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

【问题讨论】:

  • 如果你事先不知道选项,你怎么知道如何解析行?如果-b 根本不打算采用选项,则'z -x' 启动非选项('文件名')参数,并且-c-d 也是非选项(除非你喜欢在没有选项的情况下工作POSIXLY_CORRECT,但这只会让事情变得更加不确定)。我真的不认为任何人都可以在不知道允许的情况下做到这一点。你的-d 3 4 有问题; -d -3 -4 更是如此; -d 后面的负数是作为选项还是负整数?
  • @JonathanLeffler - 当然,假设任何参数都是选项参数。否则,这是一个无法解决的问题。天真的规则是任何以破折号开头的“标记/单词”都是一个新选项,并且以下所有不以破折号开头的标记都是该选项的值。
  • 我注意到Getopt::Long 说:一个特殊的条目GetOptionsFromString 可用于解析任意字符串中的选项。 ...字符串的内容通过调用Text::ParseWords::shellwords 被拆分为参数。 我不确定 Text::ParseWords::shellwords 的描述是否令人放心。
  • 我只会使用 Text::ParseWords 然后遍历结果
  • 你可以使用Getopt::Long;你会调用你假设的Getopt::Random 模块来返回一个选项字符串来传递给Getopt::Long。最棘手的一点是让GetOptions 解析成适当的变量。鉴于选项集是随机的,您需要从中收集值的变量集同样是随机的。因此,您最终可能会使用 refs 来访问哈希的某些部分,以便您可以遍历哈希的键以查找选项名称,并遍历相应的值以提取命令行信息。不平凡,但实际上并非不可能。我想!

标签: perl command-line-arguments


【解决方案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 的记忆力很长。

【讨论】:

    【解决方案2】:

    使用 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;
    }
    

    【讨论】:

    • 是的,我们现在最终做了类似的事情,但没有 Text::ParseWords。我非常希望有一个非自行骑自行车的解决方案:)
    • 是的,看到总是有奇怪的东西,我的不能正确处理-c。这就是为什么用手滚动是危险的(众所周知!)
    • 已修复,但一直被破解:/
    猜你喜欢
    • 2012-08-18
    • 2015-02-28
    • 1970-01-01
    • 2011-09-28
    • 2016-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-09
    相关资源
    最近更新 更多