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

时间:2014-12-30 10:59:07

标签: 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 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";
}

我的问题是我一次只能获得一列。我希望能够指定这样的指数选择

0, 1, 3..6, 21..33

但是它只给了我第一列。

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

perl extract.pl FILE 3 0, 1, 3..6, 21..33

3 个答案:

答案 0 :(得分:2)

在没有任何其他解决方案的情况下,我发布了一些我一直在搞乱的代码。它可以通过连接第一个字段之后的所有字段并删除所有空格和制表符来使用您的命令行。

在首先确保列集由逗号分隔的单个整数列表或由两个或三个句点分隔的起始范围组成之后,使用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";
}

答案 1 :(得分:0)

  

我的问题是我一次只能获得一列

您不明白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不会自动将string "1..3"转换为range 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

答案 2 :(得分:-1)

Perl在名为@ARGV的数组中显示在命令行中输入的参数。由于这是一个普通的数组,你可以使用这个数组的长度来获取更多信息。在子例程之外,shift命令@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?
}

现在,这不像你的方式那样花哨的裤子,你可以给范围等,但它是相当直接的:

$ perl extract.pl FILE 3 0 1 3 4 5 6 21 22 23 24 25 26 27 28 29 30 31 32 33

注意我使用了printf而不是print,所以所有字段都是相同的宽度(假设它们是字符串而且没有超过10个字符)。

我尝试寻找一个可以处理范围输入的Perl模块。我确定存在,但我找不到它。您仍然需要在@col中允许一系列输入,如上所示,然后解析@cols以获取实际列。

my $file = shift || "FILE";出了什么问题?

在你的程序中,你假设有三个参数。这意味着您需要一个文件,一行和至少一个列参数。 从不 会出现无法提供文件名的情况,因为这意味着您没有要打印的行或一组列。

因此,您需要查看$#ARGV并验证其中至少包含三个值。如果它没有三个值,您需要决定在该点做什么。简单的解决方案是通过一条消息告诉您正确的使用情况,中止该程序。您可以验证是否有一个,两个或三个参数,并决定在那里做什么。

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

...
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

顺便说一句,如果您使用GetOpt::Long,也可以使用Pod::Usage POD 代表 Plain Ol'Document ,这是Perl记录程序使用方式的方式。不妨让这种教育。阅读POD DocumentationPOD Specifications和标准POD Style。这是您记录Perl编程的方式。您可以使用perldoc命令(Betcha,您不知道它存在),打印出嵌入的Perl POD文档,并使用Pod::Usage将其打印出来供用户使用。