问题是我有 n 个命令行参数。总是至少有 2 个,但最大数量是无限的。第一个参数指定操作模式,第二个参数是要处理的文件。第三个到第 n 个是对文件执行的操作(可能没有,因为用户可能只想清理文件,如果您只向其传递 2 个参数即可完成此操作)。

我正在查看 Perl 中可用于处理数组的方法,但我不确定从第 3 项迭代到数组末尾的“Perlish”方式是什么。

我见过的一些选项:

  • 从数组末尾弹出,直到找到一个不以“-”开头的元素(因为文件路径不以“-”开头,尽管我认为可以,这可能会导致问题)。
  • 移动数组两次以删除前两个元素。无论剩下什么,如果它的大小至少为 1,我都可以迭代。

我喜欢第二个选项,但我不知道它是否是 Perlish。既然我正在尝试学习 Perl,那么我不妨学习用 Perl 做事的正确方法。

有帮助吗?

解决方案

除了使用 Sinan 所写的 Getopt 模块之外,我可能会选择:

my ( $operation, $file, @things ) = @ARGV;

然后你可以:

for my $thing_to_do ( @things ) {
...
}

其他提示

恕我直言,Perlish 完成您需要的方法是使用其中之一 CPAN 上的 Getopt 模块.

如果您仍然想手动完成,我会选择第二个选项(这类似于我们处理方法调用的第一个参数的方式):

die "Must provide filename and operation\n" unless @ARGV >= 2;

my $op = shift @ARGV;
my $file = shift @ARGV;

if ( @ARGV ) {
    # handle the other arguments;
}

我会 高度 推荐使用 Getopt::长 用于解析命令行参数。它是一个标准模块,它工作得很棒,并且使您想要做的事情变得轻而易举。

use strict;
use warnings;
use Getopt::Long;

my $first_option = undef;
my $second_option = undef;

GetOptions ('first-option=s' => \$first_option, 
            'second-option=s' => \$second_option);

die "Didn't pass in first-option, must be xxxyyyzzz."
    if ! defined $first_option;
die "Didn't pass in second-option, must be aaabbbccc."
    if ! defined $second_option;

foreach my $arg (@ARGV) {
    ...
}

这使您可以拥有一个长选项名称,并自动将信息填充到变量中,并允许您对其进行测试。它甚至允许您稍后添加额外的命令,而无需对参数进行任何额外的解析,例如添加“版本”或“帮助”选项:

# adding these to the above example...
my $VERSION = '1.000';
sub print_help { ... }

# ...and replacing the previous GetOptions with this...
GetOptions ('first-option=s' => \$first_option, 
            'second-option=s' => \$second_option)
            'version' => sub { print "Running version $VERSION"; exit 1 },
            'help' => sub { print_help(); exit 2 } );

然后,您可以使用命令行调用它 -, --, 、第一个字母或整个选项,以及 GetOptions 为您解决一切。它使你的程序更加健壮并且更容易理解;你可以说它更“可猜测”。最好的部分是您永远不必更改处理的代码 @ARGV, , 因为 GetOptions 将为您处理所有设置。

在 Perl 中执行操作的最标准方法是通过 CPAN。

所以我的第一选择是 Getopt::长. 。DevShed 上还有一个教程: 使用 Perl 处理命令行选项

您可以使用 提取第二个。到最后一项,例如:

[dsm@localhost:~]$ perl -le 'print join ", ", @ARGV[2..$#ARGV];' 1 2 3 4 5 6 7 8 9 10 00
3, 4, 5, 6, 7, 8, 9, 10, 00
[dsm@localhost:~]$ 

但是,您可能应该使用 shift (或者更好的是, GetOpt::Long)

Deepesz 的回答是一种好方法。

你的第二个选择也没有问题:

my $op     = shift; # implicit shift from @ARGV
my $file   = shift; 
my @things = @ARGV;

# iterate over @things;

您也可以跳过复制 @ARGV 进入 @things 并直接对其进行操作。然而,除非脚本非常短、非常简单,并且不太可能随着时间的推移变得更加复杂,否则我会避免走太多捷径。

无论您选择 Deepesz 的方法还是这个方法,很大程度上取决于您的品味。

决定哪个更好实际上是一个哲学问题。问题的关键是你是否应该修改全局变量,例如 @ARGV. 。有人会说,只要以高度可见的方式完成,这没什么大不了的。其他人则主张离开 @ARGV 未受影响。

不要理会任何人由于速度或内存问题而争论支持一种或另一种选择。这 @ARGV 大多数 shell 将 array 限制为非常小的大小,因此使用一种方法相对于另一种方法无法进行显着的优化。

Getopt::长, ,正如已经提到的,也是一个很好的选择。

看看 MooseX::Getopt 因为它可能会激发您对更多事物的兴趣 穆西!.

MooseX::Getopt 的示例:

# getopt.pl

{
    package MyOptions;
    use Moose;
    with 'MooseX::Getopt';

    has oper   => ( is => 'rw', isa => 'Int', documentation => 'op doc stuff' );
    has file   => ( is => 'rw', isa => 'Str', documentation => 'about file' );
    has things => ( is => 'rw', isa => 'ArrayRef', default => sub {[]} );

    no Moose;
}

my $app = MyOptions->new_with_options;

for my $thing (@{ $app->things }) {
    print $app->file, " : ", $thing, "\n";
}

# => file.txt : item1
# => file.txt : item2
# => file.txt : item3

像这样运行时会产生以上结果:

perl getopt.pl --oper 1 --file file.txt --things item1 --things item2 --things item3


这些驼鹿类型经过检查... ./getopt --oper "not a number" 产生:

Value "not a number" invalid for option oper (number expected)

并且您总是可以免费获得使用列表;-)

usage: getopt.pl [long options...]
         --file         bit about file
         --oper         op doc stuff
         --things    

/I3az/

对于任何数组的更一般情况:

for(my $i=2; $i<@array; $i++) {
    print "$array[$i]\n";
}

从第三个元素(索引 2)开始循环遍历数组。显然,您指定的具体示例,depesz 的答案是最直接、最好的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top