Conversão em massa de números em arquivos de texto usando sed

1

Atualmente, tenho vários arquivos com milhões de linhas semelhantes às seguintes:

565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

Os números 565 e 564 são ids, e extraímos todos os ids únicos de vários arquivos e os concatenamos em um único arquivo, parecendo com o seguinte:

565
564
182
982

Em seguida, desejo conduzir a seguinte conversão de números nos arquivos originais:

565 -> 1
564 -> 2
182 -> 3
982 -> 4

para que os arquivos originais se pareçam com:

1 0 10 12 23 18 17 25
2 1 7 12 13 16 18 40 29 15

Eu sei como aplicar uma única conversão usando o sed, mas há alguma maneira de especificar o modo de conversão em um arquivo de texto e usar um script de shell para aplicá-lo aos arquivos originais?

Obrigado.

    
por fredyi 06.06.2017 / 12:28

2 respostas

1

Se você quiser incrementar o primeiro campo de maneira monótona, você não precisa usar um arquivo extra para o mapeamento ou o primeiro campo, apenas use awk para definir o primeiro campo como o número da linha (registro):

awk '{$1=NR} 1' file.txt

Ele reconstruirá todo o registro com espaço como o novo separador de campo, mas, nesse caso, acho que estamos seguros, pois os campos estão separados por espaços.

Agora, aqui estão as maneiras de resolver seu problema em que você tem um arquivo extra, digamos id.txt com os primeiros campos:

Seria melhor usar algo que pudesse entender o número da linha e acompanhá-lo, por exemplo. awk :

awk 'NR==FNR {a[$0]=NR; next} {$1=a[$1]} 1' id.txt file.txt
  • assumindo, id.txt contém apenas as primeiras colunas extraídas e file.txt é o arquivo principal

  • NR==FNR {a[$0]=NR; next} salva cada registro do arquivo id.txt como chave da matriz associativa a com o valor sendo o número da linha correspondente. next garante que nenhum processamento adicional seja feito nos registros de id.txt

  • {$1=a[$1]} define o primeiro campo para o valor do elemento da matriz correspondente; note que, isto tem a ressalva de reconstruir o registro inteiro com espaço como o separador, mas eu acho que neste caso estamos seguros para alavancar essa brevidade. 1 é apenas um espaço reservado para interpretar true para que todo o registro seja impresso posteriormente

Se você se sentir chique, você pode usar algumas ferramentas * nix padrão, com a ajuda da substituição de processo ( <() ) de bash e paste finally:

paste -d' ' <(nl id.txt | cut -f1) <(cut -d' ' -f2- file.txt) 
  • nl id.txt | cut -f1 obtém o número da linha

  • cut -d' ' -f2- file.txt obtém todos os campos, exceto o primeiro

Exemplo:

% cat file.txt                                               
565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15
182 10 12 23 18 17 25
892 1 7 12 13 16 18 40 29 15

% awk '{$1=NR} 1' file.txt
1 0 10 12 23 18 17 25
2 1 7 12 13 16 18 40 29 15
3 10 12 23 18 17 25
4 1 7 12 13 16 18 40 29 15

% cat id.txt                                                 
565
564
182
892

% awk 'NR==FNR {a[$0]=NR; next} {$1=a[$1]} 1' id.txt file.txt
1 0 10 12 23 18 17 25
2 1 7 12 13 16 18 40 29 15
3 10 12 23 18 17 25
4 1 7 12 13 16 18 40 29 15

% paste -d' ' <(nl id.txt | cut -f1) <(cut -d' ' -f2- file.txt) 
 1 0 10 12 23 18 17 25
 2 1 7 12 13 16 18 40 29 15
 3 10 12 23 18 17 25
 4 1 7 12 13 16 18 40 29 15
    
por heemayl 06.06.2017 / 12:40
0
awk 'BEGIN {OFS=""} {print "s/^", $0, "/", ++count, "/"}' > pattern.sed ids.txt

Ele lerá seu arquivo "ids" e criará uma lista de ids para pesquisa e substituição usando sed .

$ cat pattern.sed 
s/^564/1/
s/^565/2/
...

Se seus IDs não forem exclusivos, você poderá usar:

sort ids.txt | uniq | awk 'BEGIN {OFS=""} {print "s/^", $0, "/", ++count, "/"}' > pattern.sed 

para torná-lo mais eficiente e, em seguida, execute:

$ sed -i.bk -f pattern.sed file 

$ cat file
2 0 10 12 23 18 17 25
1 1 7 12 13 16 18 40 29 15
1 1 7 12 13 16 18 40 29 11111
1 1 7 12 13 16 18 40 29 15555
2 0 10 12 23 18 17 2555
...

Se você deseja classificar o arquivo final, use sort -k1,1 file > file.sorted .

    
por Ravexina 06.06.2017 / 13:32