Perl脚本给出了不可预测的结果

use*_*794 1 perl

我是Perl的新手.我编写了一个脚本来显示Linux passwd文件中的用户名.它显示用户名列表,但它也显示用户ID(我现在不想显示),最后显示"用户ID和名称列表:",它应显示在显示名称列表之前.知道它为什么会这样吗?

 #!/usr/bin/perl
 @names=system("cat /etc/passwd | cut -f 1 -d :");
 @ids=system("cat /etc/passwd | cut -f 3 -d :");
 $length=@ids;
 $i=0;
 print "List of users ids and names:\n";
 while ($i < $length) {
    print $names[$i];
    $i +=1;
 }
Run Code Online (Sandbox Code Playgroud)

amo*_*mon 5

简答:system不返回命令的输出; 它返回退出值.由于cut未重定向的输出,它会打印到当前的STDOUT(例如您的终端).使用openqx//引用(又名反引号)来捕获输出:

@names = `cat /etc/passwd | cut -f 1 -d :`;
Run Code Online (Sandbox Code Playgroud)

当您还在学习Perl时,这里有一篇文章详细说明了我如何解决这个问题:

首先,始终use strict; use warnings;在脚本的开头.这有助于预防和检测许多问题,这使它成为宝贵的帮助.

接下来,启动shell时一切都可能里面的Perl做的是低效率的(你的解决方案开始6个不必要的流程(两套sh,cat,cut)).事实上,cat即使在shell版本中也没用; 只需使用shell重定向运算符:cut ... </etc/passwd.

要在Perl中打开文件,我们会这样做

use autodie; # automatic error handling
open my $passwd, "<", "/etc/passwd";
Run Code Online (Sandbox Code Playgroud)

"<"是模式(这里:阅读).该$passwd变量现在持有的文件句柄从中我们可以读到这样行<$passwd>.这些行仍然包含换行符,因此我们将chomp变量(删除行结尾):

while (<$passwd>) {  # <> operator reads into $_ by default
  chomp; # defaults to $_
  ...
}
Run Code Online (Sandbox Code Playgroud)

split内建如果匹配隔板一个正则表达式,一个字符串(默认为$_变量),以及一个可选的限制.它返回一个字段列表.要用:分隔符拆分字符串,我们会这样做

my @fields = split /:/;
Run Code Online (Sandbox Code Playgroud)

左侧不必是数组,我们也可以提供变量列表.这与右侧的列表匹配,并为每个变量分配一个元素.如果我们想跳过一个字段,我们将其命名为undef:

my ($user, undef, $id) = split /:/;
Run Code Online (Sandbox Code Playgroud)

现在我们只想打印用户.我们可以使用该print命令:

print "$user\n";
Run Code Online (Sandbox Code Playgroud)

从perl5 v10开始,我们可以使用该say功能.这与print非常相似,但会自动在输出中附加换行符:

say $user;
Run Code Online (Sandbox Code Playgroud)

瞧,我们有最后的剧本:

#!/usr/bin/perl
use strict; use warnings; use autodie; use feature 'say';

open my $passwd, "<", "/etc/passwd";

while (<$passwd>) {
  chomp;
  my ($user, undef, $id) = split /:/;
  say $user;
}
Run Code Online (Sandbox Code Playgroud)

编辑古董perls

autodie模块最终作为核心模块与v10.1一起分发.此外,feature 'say'在v10之前不可用.

因此,我们必须使用print而不是say手动错误处理:

#!/usr/bin/perl
use strict; use warnings;

open my $passwd, "<", "/etc/passwd" or die "Can't open /etc/passwd: $!";

while (<$passwd>) {
  chomp;
  my ($user, undef, $id) = split /:/;
  print "$user\n";
}
Run Code Online (Sandbox Code Playgroud)

open返回时,它失败假值.在这种情况下,$!变量将保留错误的原因.