Perl在带有-p和-f选项的脚本中处理ARGV

时间:2019-01-08 05:35:32

标签: perl command-line-arguments

我有一个用-p和-f选项调用的perl脚本。我想在脚本中将命令行参数传递给ARGV。

例如,opl.pl是一个脚本,它将不是以 xx 开头的每一行连接到以 xx 开头的前一行,并以<在标记预先存在的'#'字符后,将em>'#'作为分隔符:

# Usage: perl -pf opl.pl file.txt
BEGIN {$recmark = @ARGV[0] if $#ARGV; }
$recmark  = "xx" if (! defined $recmark);
chomp;
print "\n" if /$recmark/;
s/#/\_\_hash\_\_/g;
$_ .= "#"

当命令行上没有其他参数时,脚本将起作用。例如,perl -pf oplx.pl filexx.txtfilexx.txt

xx line #1
line 2
line 3
xx line 4
line 5

产生(大约):

xx line __hash__1#line 2#line 3
xx line 4#line 5

我想将perl -pf oplx.pl filexyy.txt yyfileyy.txt一起使用:

yy line #1
line 2
line 3
yy line 4
line 5

(大约)产生:

yy line __hash__1#line 2#line 3
yy line 4#line 5

不幸的是,perl将命令行参数yy解析为文件名,而不是参数。

3 个答案:

答案 0 :(得分:1)

-n command switch

  

使Perl在您的程序周围假设以下循环,从而使其遍历文件名参数,例如sed -nawk

LINE:
  while (<>) {
     ...        # your program goes here
  }

<> filehandle特别之处

  

<>的输入来自标准输入或命令行中列出的每个文件。

换句话说,它从命令行上给定的所有文件中读取行。 -p的作用相同,只是每次也打印$_

可以在@ARGV variable中找到这些文件名,在您的示例中,它们具有filexyy.txtyy,因此被视为文件名。

一种解决方案:在yy块中,从@ARGV删除所需的参数(此处为BEGIN)。这样,<>的操作确实将只有文件名可以使用。

这提出了程序所需界面的问题。如果您希望最后在命令行上提供该参数

my $param;
BEGIN {
    $param = pop @ARGV;
}

因为pop从数组的后面删除;如果要先给出参数,则使用shift。请注意,您的$recmark也必须从@ARGV中删除。

跟踪所有这些信息容易出错,使用和后续工作都不方便。

最好使用Getopt::Long之类的优质模块来处理这些参数。然后,您可以给它们命名,并在需要时轻松更改接口,并由模块正确检查每个调用。

还请注意,@ARGV中的文件名(使用Getopt::Long完成选项后剩下的文件名)可以处理内部所有文件中的所有行

while (<>) { ... }

使用与上述相同的<>。在脚本内部,这比-p好得多。

答案 1 :(得分:1)

perlrun(1)手册页中:

  

-p
  使Perl在您的程序周围假设以下循环,从而使其遍历文件名参数,例如sed

 LINE:
   while (<>) {
       ...             # your program goes here
   } continue {
       print or die "-p destination: $!\n";
   }

-p开关最适合用于单行代码,其中每个文件参数逐行依次处理,并将程序执行的结果打印到stdout。

-p开关隐式添加的Perl尖括号,将一个文件句柄作为输入,并遍历每一行直到到达EOF:

while(<$opened_file_handle>) {
    …
}

如何,如果未传递任何文件句柄,则尖括号将默认为@ARGV,并将每个可用参数都视为文件名。如果@ARGV为空,则<>将退回到标准输入(等效于使用<STDIN>)。

如果要在命令行上同时传递参数文件名,则有两种选择:

  1. 对参数进行排序,使与文件无关的args优先出现,如下所示:

    perl -f opt.pl ABC XYZ file1.txt file2.txt
    

在您的脚本中:

my $first = shift;  # Modifies @ARGV in-place, placing "ABC" in $first
my $second = shift; # Same again, this time plucking "XYZ" from @ARGV and putting it in `$second`
  1. 或者,使用Getopt::Long模块将非文件名参数作为开关(或“选项”)传递:

    perl -f opt.pl --foo ABC --bar XYZ  file1.txt file2.txt …
    

执行此操作的Perl代码:

use Getopt::Long;
my $foo = "";
my $bar = "";
GetOptions("foo=s" => \$foo, "bar=s" => \$bar);

使用Getopt::Long是在处理文件列表时传递参数的更简洁的方法(也是推荐的方法)。

希望这会有所帮助!

答案 2 :(得分:1)

考虑使用环境变量来代替命令行参数。

recmark=yy perl -pf opl.pl file1 file2 ...

BEGIN { $recmark = $ENV{recmark} // "xx" };
...