Como pular primeiro, última linha não em branco e linhas em branco da modificação em um arquivo?

4

Eu tenho um arquivo como este:

H|ACCT|XEC|1|TEMP|20130215035845

849002|48|1208004|100|||1

849007|28|1208004|100|||1

T|2|3



Observe que há linhas vazias extras no final do arquivo.

Eu quero substituir o valor da coluna 5 pelo valor da coluna 4 em todas as linhas, exceto a primeira e a última linha não vazia.

Não posso confiar no número de campos, pois a última linha pode ter tantos campos quanto os outros, nem nas linhas para modificar sempre começando com um número.

Eu tentei o código abaixo:

awk 'BEGIN{FS="|"; OFS="|"} {$5=$4; print}' in.txt

A saída é:

H|ACCT|XEC|1|1|20130215035845
||||
849002|48|1208004|100|100||1
||||
849007|28|1208004|100|100||1
||||
T|2|3||
||||
||||
||||

Resultado esperado:

H|ACCT|XEC|1|TEMP|20130215035845|

849002|48|1208004|100|100||1

849007|28|1208004|100|100||1

T|2|3



Como posso ignorar a primeira e a última linha não vazia de ser alterada? Eu também quero pular linhas em branco.

    
por serenesat 13.10.2017 / 14:31

4 respostas

5

Aqui você vai com awk e processa o arquivo apenas uma vez.

awk -F'|' 'NR==1{print;next} m && NF{print m}
    NF{l="\n"$0; $5=$4; m="\n"$0; c=0}; !NF{c++}
END{ print l; for (; i++<c;)print }' OFS='|' infile

Explicação:

Aqui, estamos destacando a primeira linha para substituir o valor do campo 5 th pelo valor do campo 4 th e apenas imprimi-lo e fazer next .

... se (linha seguinte atual) não for linha vazia (pelo menos contém um campo NF ), então faça um backup da linha inteira com um \n ewline adicionado l="\n"$0 primeiro próximo conjunto 5 < o valor do campo sup> th com o valor do campo 4 th $5=$4 e o último definiu como uma variável m com \n ewline adicionado m="\n"$0; ; Há uma variável c como um sinalizador contador e é usada para determinar o número de linhas vazias !NF{c++} se nenhuma linha com pelo menos um campo vista; Caso contrário, c=0 redefinirá esse contador.

Agora modificamos a linha em m variable e m && NF{print m} a imprimirá, onde na próxima etapa awk é executada e m foi definida e não está nas linhas vazias & NF (isso é usado para evitar duplicação na impressão quando a linha vazia).

No final, estamos imprimindo a última linha intacta da qual fazemos backup sempre antes de executar a substituição END{ print l; ... e o número de linhas vazias que nunca viram uma linha com um campo com looping for (; i++<c;)print }' .

Isso é muito mais curto se você não precisar de linhas vazias redundantes.

awk -F'|' 'NR==1{print;next} m && NF{print m}
    NF{l=$0; $5=$4; m=$0} END{ print l}' OFS='|' infile
    
por 13.10.2017 / 15:43
3

Com sed , contando com a segunda linha em branco:

sed '1{n;d;};/./!{H;$g;$p;d;};x;s/|/\n/4;s/\([^|]*\)\n[^|]*/|/'

Se o seu sed não entender \n na substituição, use uma nova linha literal (ou use um caractere conhecido por não fazer parte do arquivo).

Explicação:

As linhas (exceto a primeira) são coletadas no espaço de espera, quando o final do arquivo é alcançado, o espaço de espera é impresso como está, caso contrário, com a substituição desejada.

Em detalhes:

  • 1{n;d;} : Para a primeira linha, n imprime inalterada lendo a próxima linha, apenas para d elete. Por quê? Porque o espaço de espera deve conter algo para ser impresso, então ele contém uma linha vazia de qualquer maneira.
  • /./!{H;$g;$p;d;} é executado apenas para linhas vazias, anexando-se ao espaço H old. Somente para a última linha $ mova o espaço de retenção e imprima-o. Em qualquer caso, d elete para parar a execução adicional para esta linha.
  • x troca a linha não vazia com o buffer de espera, então ela é mantida lá, enquanto agora podemos processar as linhas salvas, porque sabemos que não foi a última não vazia.

  • s/|/\n/4;s/\([^|]*\)\n[^|]*/|/ executa a cópia da coluna 4 para 5 substituindo o quarto | por uma nova linha para marcá-lo e substitua os campos antes e depois da correspondência por duas vezes o campo anterior.

por 13.10.2017 / 16:30
3

Como eu disse, a maneira mais fácil é processar o arquivo duas vezes.
1º passe - pegue a linha não. para a última linha não vazia.
2ª passagem - processa todas as linhas (exceto o cabeçalho) antes da última linha não vazia que tem pelo menos cinco campos:

awk -F'|' -vc=0 'NR==FNR{if (NF){c=NR};next};
FNR>1 && NF>4 && FNR<c {$5=$4};1' OFS='|' infile infile
    
por 14.10.2017 / 13:12
0

Eu fiz a suposição, e se a linha tivesse apenas quatro colunas - a quinta coluna deveria ser adicionada, com o valor da quarta coluna. Certo?

Primeira versão - o awk é usado

awk '
BEGIN {
    FS = "|";
    OFS = "|";
} 
FNR == NR && $0 {
    last = NR;
}
FNR != NR {
    if(NF > 3 && FNR != last && FNR != 1) {
        $5 = $4;
    }
    print;
}' input.txt input.txt

O mesmo código com comentários:

awk '
BEGIN {
    FS = "|";
    OFS = "|";
} 
# The first traversing through file
# It is needed for getting the number of the last, non-empty line
FNR == NR && $0 {
    last = NR;
}
# The second traversing through file
FNR != NR {
    # if the number of fields more than 3 (therefore, the fourth column exists)
    # and the line number of the current file is not the last and not the first. 
    if(NF > 3 && FNR != last && FNR != 1) {
        $5 = $4;
    }
    print;
}' input.txt input.txt

Segunda versão - sed e tac são usados

tac input.txt | 
sed '
1,/./!{
    $!{
        s/\(|\w*\)//3
        s/|\w*//5
    }
}' | tac 

Explicação:

  1. tac - concatena e imprime os arquivos no sentido inverso. tac é um cat ao contrário.
  2. 1,/./! - pula linhas do primeiro para o primeiro não vazio (incluindo).
  3. $! - todas as linhas, exceto a última. Lembre-se que nós invertemos o arquivo, e a última linha é a primeira linha de fato.
  4. s/\(|\w*\)//3 - duplicando a quarta coluna. Decidi usar \w em vez de [^|] para beleza. Mas você pode mudá-lo, se caracteres não-palavra forem esperados nos campos.
  5. s/|\w*//5 - remove a quinta coluna anterior (agora é sexta).
  6. | tac - inverte o arquivo.
por 14.10.2017 / 02:14