如何在 perl 中使用 Getopt::Long lib 获取散列参数数组?

How to get array of hash arguments using Getopt::Long lib in perl?

我想通过在我的脚本中使用 Getopt::Long 将参数作为散列数组。 考虑以下命令行示例:

perl testing.pl --systems id=sys_1 ip_address=127.0.0.1 id=sys_2 ip_address=127.0.0.2

为了简单起见,我使用了两个系统并且每个系统只有两个子参数,即idip_address。理想情况下,systems 的数量是动态的;它可能包含 1 个、2 个或更多,每个系统的参数数量也是如此。

我的脚本应该以这样的方式处理这些参数,它将存储在 @systems 数组中,并且每个元素将是一个包含 idip_address 的散列。

Getopt::Long 中有没有什么方法可以在不自己解析的情况下实现这一点?

以下是我要实现的伪代码:


testing.pl

use Getopt::Long;
my @systems;
GetOptions('systems=s' => \@systems);
foreach (@systems) {
  print $_->{id},' ', $_->{ip_address};
}

尝试一下,可能会有更优雅的方案:

GetOptions('systems=s{1,}' => \my @temp );

my @systems;
while (@temp) {
    my $value1 = shift @temp;
    $value1 =~ s/^(\w+)=//; my $key1 = ;
    my $value2 = shift @temp;
    $value2 =~ s/^(\w+)=//; my $key2 = ;
    push @systems, { $key1 => $value1, $key2 => $value2 };
}

for (@systems) {
    print $_->{id},' ', $_->{ip_address}, "\n";
}

输出:

sys_1 127.0.0.1
sys_2 127.0.0.2

您所描述的,

--systems id=sys_1 ip_address=127.0.0.1 id=sys_2 ip_address=127.0.0.2

似乎是一个选项,它采用可变数量的成对参数,并且是二的倍数。 Getopt::Long's "Options with multiple values" 让您可以执行以下操作:

GetOptions('systems=s{2,4}' => \@systems);

这允许您指定 2、3 或 4 个参数,但它没有 "any even number of arguments" 的语法(以涵盖超过两个的任意数量的对),并且您仍然必须解压缩 id=sys_1 然后手动。您可以编写一个 user-defined subroutine 来处理 --systems' 参数的处理(但不考虑丢失的 id=...s):

my $system;
my %systems;

GetOptions('systems=s{,}' => sub {
    my $option = shift;
    my $pair = shift;
    my ($key, $value) = split /=/, $pair;

    $system = $value if $key eq 'id';
    $systems{$system} = $value if $key eq 'ip_address';
});

不过,我更喜欢以下方案之一:

--system sys_1 127.0.0.1 --system sys_2 127.0.0.1
--system sys_1=127.0.0.1 --system sys_2=127.0.0.1

它们是通过以下方式实现的:

GetOptions('system=s{2}', \@systems);
GetOptions('system=s%', \@systems);

我实际上认为这是一个设计问题,而不是 GetOpt 的问题 - 支持作为命令行参数传递的多个成对参数的概念我认为你会做得更好回避。

GetOpt 不真正支持它是有原因的 - 它确实不是一个可扩展的解决方案。

不如只读取 STDIN 中的值怎么样?:

#!/usr/bin/env perl

use strict;
use warnings;

use Data::Dumper;

my %systems = do { local $/; <DATA> } =~ m/id=(\w+) ip_address=([\d\.]+)/mg;

print Dumper \%systems; 

然后您就可以调用您的脚本:

perl testing.pl <filename_with_args>

或类似。

如果你真的必须:

my %systems = "@ARGV" =~ m/id=(\w+) ip_address=([\d\.]+)/g;

以上两个都适用于多个参数。

但是,您对另一个 post 的评论:

I can't because I'm fetching parameters from database and converting them into command line and then passing it to the script using system command $cmd_lines_args = '--system --id sys_1 --ip_address 127.0.0.0.1'; system("perl testing.pl $cmd_lines_args"); $cmd_lines_args I'll generate dynamically using for loop by reading from database

.. 这就是一个 XY 问题。

不要那样做:

open ( my $script, '|-', "perl testing.pl" );
print {$script} "id=some_id ip_address=10.9.8.7\n";
print {$script} "id=sys2 ip_address=10.9.8.7\n";

我会像这样解析 --systems arg 并引用 "hashes":

perl testing.pl --systems "id=s1 ip_address=127.0.0.1 id=s2 ip_address=127.0.0.2"

大概这样解析:

my($systems,@systems);
GetOptions('systems=s' => $systems);
for(split/\s+/,$systems){
    my($key,$val)=split/=/,$_,2;
    push @systems, {} if !@systems or exists$systems[-1]{$key};
    $systems[-1]{$key}=$val;
}
print "$_->{id} $_->{ip_address}\n" for @systems;

打印:

sys_1 127.0.0.1
sys_2 127.0.0.2