Como somar os valores de cada duas linhas em sua linha no linux

0

Eu tenho dados como este:

input.txt

1 0000100101000000
1 0000010100000000
2 1110000001000000
2 1111000000001000
3 0000000111111111
3 1111111100000000
4 8888345500000000
4 0000000000000000

e quero somar os valores dentro de cada duas linhas com o mesmo número de linha: saída:

output.txt

1 0000110201000000
2 2221000001001000
3 1111111211111111
4 8888345500000000

alguma sugestão por favor? meu dat real tinha 8000 linhas com 45000 dígitos em cada linha

    
por zara 05.10.2015 / 23:41

4 respostas

2
sed '
    N                                                       #append next line
    s/$/))/                                                 #add '))' to end
    s/\(\S*\s*\)\(.*\)\n/printf "%016d\n" \$((10#+10#/  #check Nos, form line
    t                                                       #to end if Nos equal
    s/))$//                                                 #remove '))'
    D                                                       #delete 1st line
    ' file |
bash

Em relação ao número de 45000 dígitos, por favor, note que o número máximo que o bash pode manipular é

/* Minimum and maximum values a 'signed long int' can hold.  */
#  if __WORDSIZE == 64
#   define LONG_MAX 9223372036854775807L
#  else
#   define LONG_MAX 2147483647L
#  endif

[1] /usr/include/limits.h

    
por 06.10.2015 / 10:42
2

Que tal uma solução awk :

awk 'BEGIN { tag = -1; sum = 0}
    {
        if (tag != $1) {
            if (tag > -1) {printf "%d %016d\n",  tag, sum;}
            tag = $1; sum = $2
        } else { sum += $2 }
    }
    END {print tag, sum}'  input.txt

Não está claro se sua entrada é classificada pela primeira coluna ou não. Você pode ter que fazer algo como: sort -k1.1n input.txt e depois canalizar isso no script awk acima.

    
por 06.10.2015 / 00:10
0

Ruby tem suporte a bignum, então você pode fazer

ruby -e '
    sum = Hash.new {|h,k| h[k] = 0} 
    f = File.new(ARGV.shift)
    key, val = f.readline.chomp.split
    width = val.length
    sum[key] = val.to_i
    f.each_line {|line| key,val = line.chomp.split; sum[key] += val.to_i}
    sum.keys.sort.each {|key| printf "%d %0*d\n", key, width, sum[key]}
' file
    
por 06.10.2015 / 18:09
0

Se pudermos supor que sempre há duas linhas a serem adicionadas (nunca 3 ou 1 ou qualquer outra) e que os números estão sempre na segunda coluna, separados por um espaço, existe uma solução fácil:

cut -f2 -d' ' input.txt | perl -Mbigint -nle 'print $_ + <>' > output.txt

O comando cut apenas seleciona a segunda coluna de dados e lança o primeiro fora. O comando perl faz um loop sobre as linhas de entrada (usando a opção -n ) e imprime a soma da linha atual e da próxima linha (para que funcione em grupos de dois). Observe o uso do módulo bigint para tratar as cadeias longas como números muito grandes. Finalmente, a saída é redirecionada para output.txt .

Se você precisar das linhas numeradas na saída, considere adicionar cat -n como último passo no pipeline ou adicione-o diretamente no código Perl:

cut -f2 -d' ' input.txt | perl -Mbigint -nle 'print ++$x . " " . ($_ + <>)' > output.txt

Ou se você não pode assumir que a entrada está formatada com espaços como no seu exemplo, você pode mover esse processamento para o Perl também:

perl -Mbigint -nle 's/.* //; $x=<>; $x =~ s/.* //; print $_ + $x' input.txt > output.txt
    
por 06.10.2015 / 21:03