我需要合并几个但大的文件中的块。每行包含文件名和字节偏移量。
# file begin end
foo/a 11970 12010
foo/a 22995 23035
foo/b 45090 45130
foo/b 46035 46075
foo/b 48150 48190
foo/c 16200 16240
foo/c 17550 17590
foo/c 18540 18580
foo/c 26730 26770
foo/c 34245 34285
提取可以使用tail -c
和head -c
,但这会多次重新打开同一文件,从而减慢进程。我想到的独特解决方案是编写一个程序,该程序查找每个块的开头并打印直到结尾,每个文件仅打开一次。
您有什么建议吗?
答案1
像这样的 perl 应该可以工作。在适当的地方替换文件名。
#!/usr/bin/env perl
use strict;
use warnings;
use IO::Handle;
open(my $list_fh, '<', 'somefile') or die "Failed to open list file: $!";
open(my $out_fh, '>', 'outfile') or die "Failed to open out file: $!";
my $merge_fh = IO::Handle->new();
my $cur_fname = q{};
my $buff;
while ( my $line = <$list_fh> ) {
next if $line =~ /^\s?#/;
chomp($line);
my ($fname, $begin, $end) = split(/\s+/, $line);
if ( $cur_fname ne $fname ) {
$merge_fh->close() if $merge_fh->opened();
open($merge_fh, '<', $fname) or die "Failed to open file: $!";
$cur_fname = $fname;
}
seek($merge_fh, $begin, 0);
read($merge_fh, $buff, $end - $begin);
print {$out_fh} $buff or die "Failed to write to $cur_fname: $!";
}
$merge_fh->close();
$out_fh->close();
$list_fh->close();
答案2
和zsh
:
zmodload zsh/mapfile
while read -r f b e; do
[ -f $f ] && printf %s ${${mapfile[$f]}[b+1,e+1]}
done < list.txt > merged
不过不要太热情。$mapfile
确实使用mmap
但读取内存中的整个文件。 (info zsh 'The zsh/mapfile Module'
详情请参阅)。
和ksh93
:
PATH=/opt/ast/bin:$PATH export PATH
while read -r f b e; do
[[ -f $f ]] && head -c "$((e-b+1))" < "$f" <#(($b))
done < list.txt > merged
以这种方式设置 PATH 以便head
成为 ksh93 内置命令(即使没有/opt/ast/bin
目录)。<#((n))
是 ksh93 的前端lseek
。
PATH=/opt/ast/bin:$PATH export PATH
while read -r f b e; do
[[ -f $f ]] && head -c "$((e-b+1))" -s "$b" < "$f"
done < list.txt > merged
ksh93
可以head
选择-s
跳过数据(lseek
内部用于常规文件)。只要ksh93
是使用内置函数构建的,它就可以工作head
。