如何在本机Perl代码中复制cat / sort / uniq?

时间:2015-02-15 15:13:46

标签: perl hash

我的基础是前一个问题中分享的知识:What native Perl code replaces `cut`?

Perl脚本使用以下代码:

my $cmd = "cat $TMPDIR/files.* | sort | uniq > $File"
`$cmd`

我正在尝试使用本机Perl重建上述函数以在MS Windows上运行。到目前为止,我有这个,但它不是很有效:

my $globPat = "$TMPDIR/parts.*"
my $outFile = "$TMPDIR/out.txt"
my %lines;

# 1) glob all files
while (my $glob = glob($globPat)) {
    open(IN, "<", "$glob") or die("Can't read $glob");
    # collect lines as unique keys in a hash
    ++$lines{ ($_)[1] } while <IN>;
    close(IN);
}

# sort the key and save values to $glueFile
open(OUT, ">", "$outFile") or die("ERROR: Can't write $outFile");
foreach my $key (sort keys %lines) {
    print OUT $lines{$key} . "\n";
}
close(OUT)

当我尝试进行故障排除时,我遇到了各种错误(行号)。有人可以帮忙理清1)如何正确使用glob,2)如何将从各种文件中读取的行添加到一个散列键中; 3)对散列键(行)进行排序并将它们打印到新的输出文件中。

4 个答案:

答案 0 :(得分:2)

您可以使用单行实现它,并使用END块进行排序,例如:

perl -ne '$h{ $_ } = 1; END { print sort keys %h }' $TMPDIR/files.*

答案 1 :(得分:2)

List::MoreUtils::uniq可以使用相同的名称完成函数的工作。对于cat,我只想使用<>。当然,你应该知道这是对猫的无用的使用。你有。排序为sort

use strict;
use warnings;
use List::MoreUtils qw(uniq);

my @list = uniq(<>);
my @sorted = sort @list;

print @sorted;

请注意,您不必在行中添加换行符,因为它们已经有换行符。

如果您不想使用该模块,uniq的代码非常简单,可以复制/粘贴。

sub uniq {
    my %seen;
    grep { not $seen{$_}++ } @_;
}

答案 2 :(得分:2)

您的代码存在一些问题

  • 我假设您从++$lines{ ($_)[1] }之类的内容中推断了++$lines{ (split)[1] }这个表达式。但是有一点不同,因为split会返回字段的列表($_)[1]正在尝试从单元素列表中提取第二个元素。您只需要++$lines{$_}

  • print OUT $lines{$key}中,您正在打印哈希%lines。但它仅用作创建唯一列表的设备,值只是每行在文件中出现的次数。您需要,因此print OUT $key, "\n"是正确的

还有一些不良做法的例子不能阻止你的程序正常工作,但无论如何都应该修复。

  • 局部变量应仅使用小写字母,数字和下划线。大写字母保留用于全局标识符

  • 您应该使用 lexical 文件句柄,例如open my $in_fh, ...而不是open IN, ...。全局变量一般来说都是一个坏主意,它也消除了在其范围结束时close文件句柄的需要,因为它会自动发生

  • 当I / O操作失败时,总是$!放入die字符串。仅使用die $!通常就足够了,因为输出包含源文件名和行号

  • 最好使用File::Spec::Functions中的catfile而不是仅使用字符串连接。它可以正确处理多个路径分隔符之类的内容,也可以更清晰地阅读

  • 你不应该在裸变量周围加上引号。例如,open(IN, "<", "$glob")应为open(IN, "<", $glob)。添加引号最多也没有区别,最糟糕的是它会为您提供完全不同的字符串

这就是我重构你的程序的方法

use strict;
use warnings;

use File::Spec::Functions 'catfile';

my $temp_dir = '.';

my $glob_pat = catfile($temp_dir, 'parts.*');
my $out_file = catfile($temp_dir, 'out.txt');

my %lines;

while ( my $parts_file = glob($glob_pat) ) {
    open my $in_fh, '<', $parts_file or die qq{Can't read "$parts_file": $!};
    ++$lines{$_} while <$in_fh>;
}

open my $out_fh, '>', $out_file or die qq{ERROR: Can't write to "$out_file": $!};
for my $line (sort keys %lines) {
    print $out_fh $line, "\n";
}

close $out_fh;

答案 3 :(得分:1)

您也可以这样使用glob

my @files = glob("$TMPDIR/parts.*");
foreach my $file (@files)
{
    open my $fh, "<", $file or die "couldn't open '$file': $!";
    while (<$fh>)
    {
        #do whatever you want to do;
    }
}