在单个命令行参数中向Perl程序传递多个值

在单个命令行参数中向Perl程序传递多个值,perl,Perl,我有一个Perl程序,它从特定行开始的特定列中提取数据 #!/usr/bin/perl # This script is to pick the specific columns from a file, starting from a specific row # FILE -> Name of the file to be passed at run time. # rn -> Number of the row from where the data has to be

我有一个Perl程序,它从特定行开始的特定列中提取数据

#!/usr/bin/perl

# This script is to pick the specific columns from a file, starting from a specific row

# FILE -> Name of the file to be passed at run time.
# rn   -> Number of the row from where the data has to be picked.

use strict;
use warnings;

my $file = shift || "FILE";
my $rn   = shift;
my $cols = shift;

open(my $fh, "<", $file) or die "Could not open file '$file' : $!\n";

while (<$fh>) {

    $. <= $rn and next;

    my @fields = split(/\t/);
    print "$fields[$cols]\n";
}
但它只给了我第一栏

我正在运行此命令以执行脚本

perl extract.pl FILE 3 0, 1, 3..6, 21..33
我的问题是,我一次只能得到一列

您不了解perl从命令行传递给程序的是什么:

use strict;
use warnings;
use 5.016;

my $str = "1..3";
my $x = shift @ARGV;  # $ perl myprog.pl 1..3 

if ($str eq $x) {
    say "It's a string";
}
else {
    say "It's a range";
}

my @cols = (0, 1, 2, 3, 4);
say for @cols[$str];

--output:--

$perl myprog.pl 1..3

Scalar value @cols[$str] better written as $cols[$str] at 1.pl line 16.

It's a string

Argument "1..3" isn't numeric in array slice at 1.pl line 16.
1
在命令行上编写的任何内容都将作为字符串传递给程序,perl不会自动将
字符串“1..3”
转换为
范围1..3
(事实上,您的字符串将是外观奇怪的
“1..3”
)。抛出一些错误后,perl会在字符串
“1..3”
的前面看到一个数字,因此perl会将字符串转换为整数1。因此,您需要自己处理字符串:

use strict;
use warnings;
use 5.016;

my @fields = (0, 1, 2, 3, 4);

my $str = shift @ARGV;   # perl myprog.pl 0,1..3  => $str = "0,1..3"
my @cols = split /,/, $str;  

for my $col (@cols) {

    if($col =~ /(\d+) [.]{2} (\d+)/xms) {
        say @fields[$1..$2];  # $1 and $2 are strings but perl will convert them to integers
    }
    else {
        say $fields[$col];
    }

}

--output:--
$ perl myprog.pl 0,1..3
0
123

在没有任何其他解决方案的情况下,我发布了一些我一直在处理的代码。它通过连接第一个字段之后的所有字段并删除所有空格和制表符,与您描述的命令行一起工作

首先确保列集合由逗号分隔的单个整数列表或由两个或三个句点分隔的开始-结束范围组成,然后使用
eval
将列集合转换为整数列表

use strict;
use warnings;
use 5.014;    # For non-destructive substitution and \h regex item

my $file = shift || "FILE";
my $rn   = shift || 0;
my $cols = join('', @ARGV) =~ s/\h+//gr;

my $item_re = qr/ \d+ (?: \.\.\.? \d+)? /ax;
my $set_re  = qr/ $item_re (?: , $item_re )* /x;
die qq{Invalid column set "$cols"} unless $cols =~ / \A $set_re \z /x;
my @cols = eval $cols;

open my $fh, '<', $file or die qq{Couldn't open "$file": $!};

while (<$fh>) {

    next if $. <= $rn;

    my @fields = split /\t/;
    print "@fields[@cols]\n";
}
使用严格;
使用警告;
使用5.014;#用于非破坏性替换和\h正则表达式项
我的$file=shift | |“file”;
我的$rn=shift | | 0;
我的$cols=join(“”,@ARGV)=~s/\h+//gr;
我的$item\u re=qr/\d+(?:\.\.\.\.?\d+)/斧头;
我的$set\u re=qr/$item\u re(?:,$item\u re)*/x;
除非$cols=~/\A$set\u re\z/x,否则死qq{无效列集“$cols”};
my@cols=eval$cols;

打开我的$fh,“Perl以一个名为。由于这是一个普通数组,因此可以使用该数组的长度来获取其他信息。在子例程外部,当您不给它任何参数时,该命令会从
@ARGV
数组的开头移动值

你可以这样做:

my $file = shift;   # Adding || "FILE" doesn't work. See below
my $rn   = shift;
my @cols = @ARGV;
cols不再是标量变量,而是一个可以容纳所有所需列的数组。换句话说,第一个参数是文件名,第二个参数是行,最后一组参数是所需的列:

while (<$fh>) {

    next if $. <= $rn;

    my @fields = split(/\t/);
    for my $column ( @columns ) {
        printf "%-10.10s", $fields[$column];
    }
    print "\n";
    break;      # You printed the row. Do you want to stop?
}
注意,我使用了而不是
print
,因此所有字段的宽度都相同(假设它们是字符串,并且没有一个长度超过10个字符)

我试图寻找一个Perl模块,它可以像您所希望的那样处理范围输入。我肯定有一个,但我找不到。您仍然需要在
@col
中允许一系列输入,如我上面所示,然后解析
@cols
以获得实际列

我的$file=shift | |“file”有什么问题? 在您的程序中,假设有三个参数。这意味着您需要一个文件、一行和至少一个列参数。您将永远不会遇到不提供文件名的情况,因为这意味着您没有要打印的行或列集

因此,您需要查看
$#ARGV
,并验证其中至少包含三个值。如果它没有三个值,则需要决定在该点上执行什么操作。简单的解决方法是,只需中止程序,并显示一条小消息,告诉您正确的用法。您可以验证是否有一个、两个或三个参数,并决定在那里做什么

另一个想法是使用,它将允许您使用命名参数。您可以使用预定义的默认值加载参数,然后在读取参数时进行更改:

...
use Getopt::Long;

my $file = "FILE";      # File has a default;
my $row, @cols;         # No default values;
my $help;               # Allow user to request help

GetOptions (
    "file=s"    => \$file,
    "rows=i     => \$rows,
    "cols=i"    => \@cols,
    "help"      => $help,
);

if ( "$help" ) {
    print_help();
}
if ( not defined $rows ) {
    error_out ( "Need to define which row to fetch" );
}
if ( not @cols ) {
    error_out ( "Need to define which rows" );
}
用户可以通过以下方式调用:

    $ perl extract.pl -file FILE -row 3 -col 0 -col 1 3 4 5 6 21 22 23 24 25 26 27 28 29 30 31 32 33
请注意,如果我使用
-col
,默认情况下,
GetOptions
将假定
-col
后面的所有值都用于该选项。另外请注意,如果需要,我可以为每列重复
-col


顺便说一下,如果你使用,你也可以使用。POD代表纯Ol’Document,这是Perl记录程序如何使用的方式。也许可以让这更具教育意义。请仔细阅读、阅读和标准。这就是您记录Perl编程的方式。您可以使用
perldoc
命令(如果您不知道它的存在),打印出嵌入的Perl POD文档,并使用为用户打印出来。

您的示例命令似乎没有
文件
参数。您的代码将设置
$file='3'
$rn='0',
$cols='1',
。这肯定不是你想要的?这篇文章有很多错误。我开始做一些小的修正,直到我意识到这项工作有多大。显然,您还没有测试任何Perl代码。“GetOptions将假定
-col
后面的所有值都用于该选项。”不,您必须指定将有多个值,或者只取第一个值,除非您对每个值重复该选项:“选项可以同时获取多个值,例如
--坐标52.2 16.4--rgbcolor 255 255 149
。这可以通过在选项规范中添加重复说明符来实现。“我应该更清楚一点。您需要有重复说明符来完成此操作。但是,可以使用单个参数指定多个值。
...
use Getopt::Long;

my $file = "FILE";      # File has a default;
my $row, @cols;         # No default values;
my $help;               # Allow user to request help

GetOptions (
    "file=s"    => \$file,
    "rows=i     => \$rows,
    "cols=i"    => \@cols,
    "help"      => $help,
);

if ( "$help" ) {
    print_help();
}
if ( not defined $rows ) {
    error_out ( "Need to define which row to fetch" );
}
if ( not @cols ) {
    error_out ( "Need to define which rows" );
}
    $ perl extract.pl -file FILE -row 3 -col 0 -col 1 3 4 5 6 21 22 23 24 25 26 27 28 29 30 31 32 33