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

时间:2013-07-20 19:23:38

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

3 个答案:

答案 0 :(得分:5)

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

@names = `cat /etc/passwd | cut -f 1 -d :`;

当你还在学习Perl时,这里有一篇详细介绍我如何解决这个问题的文章:

首先,始终在脚本开头use strict; use warnings;。这有助于预防和发现许多问题,这使它成为无价的帮助。

接下来,当在Perl中完成所有事情时启动shell是低效的(您的解决方案启动了六个不必要的进程(两组shcatcut))。实际上,cat即使在shell版本中也没用;只需使用shell重定向运算符:cut ... </etc/passwd

要在Perl中打开文件,我们将

use autodie; # automatic error handling
open my $passwd, "<", "/etc/passwd";

"<"是模式(此处:阅读)。 $passwd变量现在包含文件句柄,我们可以从中读取<$passwd>之类的行。这些行仍然包含换行符,因此我们将chomp变量(删除行结尾):

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

split内置函数采用与分隔符匹配的正则表达式,字符串(默认为$_变量)和可选限制。它返回一个字段列表。要使用:分隔符拆分字符串,我们将执行

my @fields = split /:/;

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

my ($user, undef, $id) = split /:/;

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

print "$user\n";

从perl5 v10开始,我们可以使用say功能。其行为与print完全相同,但会自动在输出中附加换行符:

say $user;

瞧,我们有最后的剧本:

#!/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;
}

编辑古董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";
}

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

答案 1 :(得分:3)

要读取系统数据库,您应该使用适当的系统功能:

use feature qw(say);

while (
    my ($name,    $passwd, $uid, $gid,   $quota,
        $comment, $gcos,   $dir, $shell, $expire
    )
    = getpwent
    )
{
    say "$uid $name";
}

答案 2 :(得分:0)

如果您正在扫描整个密码文件,可以使用getpwent()

while( my @pw = getpwent() ){
  print "@pw\n";
}

请参阅perldoc -f getpwent