我的基础是前一个问题中分享的知识: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)对散列键(行)进行排序并将它们打印到新的输出文件中。
答案 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;
}
}