Mesclar partes de vários arquivos

1

Eu preciso mesclar partes de alguns arquivos grandes. Cada linha contém o nome do arquivo e os offsets de bytes.

# 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

A extração poderia usar tail -c e head -c , mas isso reabriria o mesmo arquivo várias vezes, diminuindo o processo. A solução única em que pensei foi a codificação de um programa que buscava o início de cada parte e imprimia até o final, abrindo cada arquivo apenas uma vez.

Você tem alguma sugestão?

    
por Pedro Lacerda 31.03.2013 / 22:12

2 respostas

2

Algo como esse perl deve funcionar. Substitua nomes de arquivos quando apropriado.

#!/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();
    
por 31.03.2013 / 23:05
2

com 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

Não fique muito entusiasmado. $mapfile usa mmap , mas lê todo o arquivo na memória. (veja info zsh 'The zsh/mapfile Module' para detalhes).

com 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

Configurar o PATH dessa forma é para que head seja o ksh93 embutido (mesmo que não exista nenhum diretório /opt/ast/bin ). <#((n)) é o frontend do ksh93 para 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 tem a opção -s para pular dados (usa lseek internamente para arquivos regulares). Funcionaria desde que o ksh93 tenha sido construído com o head incorporado incluído.

    
por 31.03.2013 / 23:28