Substitua várias linhas em um arquivo de texto por um padrão fixo

2

Ok, procurando uma maneira rápida de fazer isso. Eu tenho uma lista de números de linha que precisam ser alterados em um arquivo de dados de texto. O padrão de 16 bits nessa linha pode ser qualquer coisa, mas eu preciso mudar para alterá-lo para ler XXXXXXXXXXXXXXXX com base apenas no número da linha. Novamente eu tenho uma longa lista de números de linha que eu sei que precisam ser alterados. Não há padrão discernível para as linhas que precisam ser alteradas. (Eu não escrevi o padrão de dados, mas sei que linhas precisam ser alteradas para ler todos os Xs.) Eu li muitas respostas aqui e nenhuma realmente lida com isso.

Se isso não está claro, vou colocar de outra forma ...

Altere as linhas 26115, 32198, 37256, 40001, 40023 em um arquivo para ler XXXXXXXXXXXXXXX . Tenho mais de 100.000 linhas que precisam ser alteradas em um arquivo de linha de 1,9 milhão.

    
por Thorne Kontos 08.05.2017 / 01:42

4 respostas

3

Como uma extensão da resposta do @Gilles, já que você diz que você tem os números de linha que precisam ser alterados em um arquivo (que eu assumirei como classificado e chamado linums )

awk '
  BEGIN { getline NEXT < "linums" }
  NR == NEXT { $0 = "XXXXXXXXXXXXXXX"; getline NEXT < "linums" }
  1
'

Isso se adapta bem à alteração de milhares de linhas sem a necessidade de digitar manualmente esses milhares de números de linha.

Como alternativa, com uma pequena modificação, você pode levar os números de linha ou o arquivo a ser alterado em stdin . Eu faria um script para isso (eu chamei de redact.awk )

#!/usr/bin/awk -f
BEGIN {
    LINUMS = ARGV[1]
    ARGV[1] = ARGV[2]
    --ARGC
    getline NEXT < LINUMS
}
NR == NEXT {
    $0 = "XXXXXXXXXXXXXXX"
    getline NEXT < LINUMS
}
1

Então você pode usar qualquer um dos seguintes:

$ ./redact.awk linums file-to-be-changed
$ ./redact.awk - file-to-be-changed
$ ./redact.awk linums -
$ ./redact.awk linums

(Os dois últimos são equivalentes)

    
por 08.05.2017 / 04:17
2

O sed ou o awk funciona bem para essa tarefa.

sed '
    26115 s/.*/XXXXXXXXXXXXXXX/
    32198 s/.*/XXXXXXXXXXXXXXX/
    37256 s/.*/XXXXXXXXXXXXXXX/
    40001 s/.*/XXXXXXXXXXXXXXX/
    40023 s/.*/XXXXXXXXXXXXXXX/
'
awk '
  NR==26115 || NR==32198 || NR==37256 || NR==40001 || NR==40023 {$0 = "XXXXXXXXXXXXXXX"}
  1
'

(O único 1 imprime todas as linhas, após a possível transformação realizada pelo código anterior.)

    
por 08.05.2017 / 01:51
2
sed -e '1{x;s/^/XXXXXXXXXXXXXXX/;x;}
   26115bp
   32198bp
   37256bp
   40001bp
   40023bp
   d
   :p
   g
' data_file

Primeiro, colocamos o espaço de espera com o padrão desejado XXXXXXXXX e, em seguida, lembramos que apenas para os números de linha desejados, pulando para o rótulo: p, que recuperará o espaço de retenção que será transferido implicitamente para stdout. As linhas não correspondentes são excluídas (altere o d para b se você quiser mantê-las).

    
por 08.05.2017 / 08:35
1

Como a substituição é estática e como a operação de substituir várias linhas é tão simples em sed , é possível criar um grande script sed para fazer o trabalho.

Supondo que você tenha os números de linha em um arquivo separado, linenos.txt , um número de linha por linha, então podemos produzir o script (% GNU) sed até

$ awk '{ printf("%dc XXXXXXXXXXXXXXX\n", $0) }' linenos.txt >script.sed

ou

$ awk '{ print $0, "c XXXXXXXXXXXXXXX" }' linenos.txt >script.sed

Então, é questão de aplicá-lo em um arquivo:

$ sed -f script.sed file >file.new

Nota: Eu nunca executei um script sed excessivamente grande, então eu não sei como o sed do GNU lida com isso no desempenho.

    
por 08.05.2017 / 09:41