Perl 在使用 -p 和 -f 选项调用的脚本中处理 ARGV

Perl process ARGV in a script called with -p and -f options

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

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

# 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 解析为文件名,而不是参数。

-n command switch

causes Perl to assume the following loop around your program, which makes it iterate over filename arguments somewhat like sed -n or awk:

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

其中 <> filehandle

不同

Input from <> comes either from standard input, or from each file listed on the command line.

换句话说,它从命令行中给定的所有文件中读取行。 -p 做同样的事情,只是它每次都打印 $_

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

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

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

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

pop removes from the back of an array; if you want the parameter to be given first then use shift。请注意,您的 $recmark 也必须从 @ARGV.

中删除

跟踪所有这些很容易出错,而且不方便使用和进一步工作。

最好使用一个好的模块来处理这些参数,例如 Getopt::Long。然后您可以给它们命名,根据需要轻松更改接口,并让模块正确检查每个调用。

另请注意,@ARGV 中的文件名是您(或 Getopt::Long)完成选项后剩下的内容,您可以处理 [=40= 中所有文件的所有行]

while (<>) { ... }

使用与上述相同的 <>。在脚本中,这比 -p.

好得多

来自 perlrun(1) 手册页:

-p
causes Perl to assume the following loop around your program, which makes it iterate over filename arguments somewhat like sed:

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

-p 开关最合适的用法是单行,其中每个文件参数依次逐行处理,并将程序执行的结果打印到标准输出。

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

while(<$opened_file_handle>) {
    …
}

HOWEVER,如果没有传递文件句柄,尖括号将默认为 @ARGV,将每个可用参数视为文件名。如果@ARGV 为空,<> 回退到标准输入(相当于使用 <STDIN>)。

如果您想在命令行上同时传递参数 文件名,您有两个选择:

  1. 对参数进行排序,使与文件无关的参数排在第一位,如下所示:

    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 模块将非文件名参数作为开关传递(或 "options"):

    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 是在处理文件列表时传递参数的更简洁(也是推荐的方式)。

希望对您有所帮助!

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

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

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