Sed / awk / perl: invertendo a ordem dos valores separados por vírgula, preservando outro texto

2

Eu tenho este texto:

LABEL1
    .BYTE 01, 02, 03, 04, 05
    .BYTE 01, 02, 03

Eu preciso reverter apenas a ordem dos valores separados por vírgula:

LABEL1
    .BYTE 05, 04, 03, 02, 01
    .BYTE 03, 02, 01

Eu preciso disso trabalhando com coisas como:

ITINERARY_ARRAY_01
    .BYTE <ITINERARY_00A
    .BYTE <ITINERARY_01A
    .BYTE <ITINERARY_02A
    .BYTE <ITINERARY_03A
    .BYTE <ITINERARY_04A
    .BYTE <ITINERARY_05A
    .BYTE <ITINERARY_06A
    .BYTE <ITINERARY_07A
    .BYTE <ITINERARY_08A
    .BYTE <ITINERARY_09A
    .BYTE <ITINERARY_10A
    .BYTE <ITINERARY_11A
    .BYTE <ITINERARY_12A
    .BYTE <ITINERARY_13A
    .BYTE <ITINERARY_14A
;-------------------
ITINERARY_01E
    .BYTE $03, $05, $07, $00
;-------------------
ITINERARY_01F
    .BYTE $03, $05, $07, $09, $00
;-------------------
ITINERARY_01G
    .BYTE $28, $0D, $00
;-------------------
ITINERARY_01H
    .BYTE $28, $0D, $0F, $13, $00
;-------------------
ITINERARY_01I
    .BYTE $28, $0D, $0F, $11, $00
;-------------------
ITINERARY_01J
    .BYTE $03, $05, $07, $09, $20, $1E, $00
;-------------------
ITINERARY_01K
    .BYTE $28, $0D, $0F, $13, $15, $00
;-------------------
ITINERARY_01L
    .BYTE $03, $05, $07, $09, $20, $1E, $1C, $27
    .BYTE $00
;---------------------

em que nada precisa ser alterado, exceto os valores depois de ".BYTE", que devem estar em ordem reversa, formato hexadecimal, usando "$" como prefixo ... Desculpe por isso "editar", mas eu vejo isso só agora. Obrigado novamente!

    
por Flavio Pasqualin 18.10.2014 / 09:07

4 respostas

2

Arquivo revbytes2.awk :

#!/usr/bin/awk -f
BEGIN {
        FS=",? +"
}
NF>2 && match($0,"^ +\.BYTE ") {
        printf substr($0,1,RSTART+RLENGTH-1)
        for(i=NF;i>3;i--) printf $i", "
        print $3
        next
}
1

FS=",? +" faz com que awk reconheça o espaço depois de .BYTE e a sequência de espaço , plus entre os bytes como separador de campo.

Para cada linha, procuraremos linhas com mais de dois campos, começando com espaços seguidos por .BYTE e um espaço e rember o início e a duração desse prefixo em RSTART e RLENGTH como efeito colateral do% expressãomatch(...).

Se essa correspondência for encontrada e houver mais de dois campos, o prefixo será cortado da linha original usando RSTART e RLENGTH e será impresso, seguido dos demais campos na ordem inversa.

Se os espaços mais .BYTE mais prefixo de espaço não forem encontrados ou se não houver mais de dois campos, a linha será impressa como está. Então, isso será feito para uma .BYTE -line definindo apenas um byte também, porque não há nada para reverter.

Execução de teste:

$ diff -u$(wc -l <input) input <(awk -f revbytes2.awk input)
--- input       2014-10-19 06:04:48.280714146 +0200
+++ /dev/fd/63  2014-10-19 22:40:01.385538235 +0200
@@ -1,42 +1,42 @@
 ITINERARY_ARRAY_01
     .BYTE <ITINERARY_00A
     .BYTE <ITINERARY_01A
     .BYTE <ITINERARY_02A
     .BYTE <ITINERARY_03A
     .BYTE <ITINERARY_04A
     .BYTE <ITINERARY_05A
     .BYTE <ITINERARY_06A
     .BYTE <ITINERARY_07A
     .BYTE <ITINERARY_08A
     .BYTE <ITINERARY_09A
     .BYTE <ITINERARY_10A
     .BYTE <ITINERARY_11A
     .BYTE <ITINERARY_12A
     .BYTE <ITINERARY_13A
     .BYTE <ITINERARY_14A
 ;-------------------
 ITINERARY_01E
-    .BYTE $03, $05, $07, $00
+    .BYTE $00, $07, $05, $03
 ;-------------------
 ITINERARY_01F
-    .BYTE $03, $05, $07, $09, $00
+    .BYTE $00, $09, $07, $05, $03
 ;-------------------
 ITINERARY_01G
-    .BYTE $28, $0D, $00
+    .BYTE $00, $0D, $28
 ;-------------------
 ITINERARY_01H
-    .BYTE $28, $0D, $0F, $13, $00
+    .BYTE $00, $13, $0F, $0D, $28
 ;-------------------
 ITINERARY_01I
-    .BYTE $28, $0D, $0F, $11, $00
+    .BYTE $00, $11, $0F, $0D, $28
 ;-------------------
 ITINERARY_01J
-    .BYTE $03, $05, $07, $09, $20, $1E, $00
+    .BYTE $00, $1E, $20, $09, $07, $05, $03
 ;-------------------
 ITINERARY_01K
-    .BYTE $28, $0D, $0F, $13, $15, $00
+    .BYTE $00, $15, $13, $0F, $0D, $28
 ;-------------------
 ITINERARY_01L
-    .BYTE $03, $05, $07, $09, $20, $1E, $1C, $27
+    .BYTE $27, $1C, $1E, $20, $09, $07, $05, $03
     .BYTE $00
 ;---------------------

Comparando mawk e gawk output:

$ diff <(mawk -f revbytes2.awk input) <(gawk -f revbytes2.awk input)
gawk: revbytes2.awk:5: warning: escape sequence '\.' treated as plain '.'

Obviamente não há diferenças no stdout. Bom!

O aviso desaparece se você escrever "^ +6BYTE " em vez de "^ +\.BYTE " dentro da expressão match(...) .

Talvez alguém que use gawk conheça com frequência uma maneira melhor de evitar o aviso.

    
por 19.10.2014 / 07:08
3

Aqui eu faço isso com sed :

sed '/,/!b                                                   
s/\( *[^ ]*\)\(.*\)/,\n/;:t
s/\([^,]*,\)\(\n.*\)//;tt
s/\n\(.*\),//' <<\DATA
LABEL1
    .BYTE 01, 02, 03, 04, 05
    .BYTE 01, 02, 03        
LABEL1
    .BYTE 01, 02, 03, 04, 05
    .BYTE 01, 02, 03
DATA

OUTPUT

LABEL1
    .BYTE 05, 04, 03, 02, 01 
    .BYTE 03, 02, 01 
LABEL1
    .BYTE 05, 04, 03, 02, 01 
    .BYTE 03, 02, 01 

Verifica por uma vírgula na linha atual. Se houver ! sem vírgula, sed b sairá do script e imprimirá automaticamente a linha. Se a linha contiver uma vírgula sed faz o seguinte:

  1. Ele primeiro prepara a linha por s/// ubstituting o seguinte:
    • \( *[^ ]*\) - a primeira sequência ocorrente de zero ou mais espaços seguidos imediatamente por uma sequência de zero ou mais caracteres não espaciais referenciados como seguidos imediatamente por ...
    • \(.*\) - todo o resto na linha referenciada como ...
    • ... com ,\n
    • Observação - usando o escape \n no campo s/// ubstituição do lado direito, como se este não fosse totalmente portátil. Para um sed que não o suporta, pode ser feito substituindo uma nova linha literal pelo n na instrução.
  2. Ele : define uma etiqueta de ramificação / teste chamada t .
  3. Embora ainda seja possível, sed s/// ubstitutes:
    • \([^,]*,\) - Uma seqüência de zero ou mais não caracteres vírgula então uma única vírgula referenciada como seguida imediatamente por ...
    • \(\n.*\) - Uma sequência que começa com pelo menos um caractere de \n ewline seguido por qualquer coisa / tudo restante no espaço de padrão referenciado como ...
    • ... com .
  4. Se a% anteriors/// ubstitution t ests tiver êxito, sed ramificará de volta para o rótulo :t est e tentará novamente.
  5. O último sed faz uma pequena limpeza e substitui:
    • \n\(.*\), - o primeiro caractere \n ewline a ocorrer e a última vírgula a ocorrer ...
    • - ... com tudo o que fica entre.

Como sed faz a substituição recursiva, o delimitador de ewline \n avança um campo delimitado por vírgula por vez. Ele interrompe as substituições quando o \n ewline é o primeiro caractere na linha. Aqui está um l ook no seu progresso através do processo recursivo de substituição:

 01, 02, 03, 04, 05,\n    .BYTE$
 01, 02, 03, 04,\n    .BYTE 05,$
 01, 02, 03,\n    .BYTE 05, 04,$
 01, 02,\n    .BYTE 05, 04, 03,$
 01,\n    .BYTE 05, 04, 03, 02,$
\n    .BYTE 05, 04, 03, 02, 01,$

Após sua substituição preparatória inicial, sed não delimita nada além de vírgulas e o caractere \n ewline inserido. Então, qualquer tipo de valores separados por vírgula funcionam bem. Aqui está a saída da execução de sua longa passagem:

ITINERARY_ARRAY_01
    .BYTE <ITINERARY_00A
    .BYTE <ITINERARY_01A
    .BYTE <ITINERARY_02A
    .BYTE <ITINERARY_03A
    .BYTE <ITINERARY_04A
    .BYTE <ITINERARY_05A
    .BYTE <ITINERARY_06A
    .BYTE <ITINERARY_07A
    .BYTE <ITINERARY_08A
    .BYTE <ITINERARY_09A
    .BYTE <ITINERARY_10A
    .BYTE <ITINERARY_11A
    .BYTE <ITINERARY_12A
    .BYTE <ITINERARY_13A
    .BYTE <ITINERARY_14A
;-------------------
ITINERARY_01E
    .BYTE $00, $07, $05, $03 
;-------------------
ITINERARY_01F
    .BYTE $00, $09, $07, $05, $03 
;-------------------
ITINERARY_01G
    .BYTE $00, $0D, $28 
;-------------------
ITINERARY_01H
    .BYTE $00, $13, $0F, $0D, $28 
;-------------------
ITINERARY_01I
    .BYTE $00, $11, $0F, $0D, $28 
;-------------------
ITINERARY_01J
    .BYTE $00, $1E, $20, $09, $07, $05, $03 
;-------------------
ITINERARY_01K
    .BYTE $00, $15, $13, $0F, $0D, $28 
;-------------------
ITINERARY_01L
    .BYTE $27, $1C, $1E, $20, $09, $07, $05, $03
    .BYTE $00
;---------------------
    
por 19.10.2014 / 05:42
2

Veja como eu faria:

perl -MTie::File -e'
    tie @lines,"Tie::File","your_file";
    for(@lines){
        next unless /,/; # Skip lines with no commas
        $csv = /(\s*[^,\s]+,.*)/;
        $new_csv = join ",",reverse split /,/,$csv;
        s/\Q$csv/$new_csv/;
    }'

Aviso legal

Isto irá modificar o seu arquivo in situ . Use uma cópia fictícia do arquivo, se isso não for desejado.

Uma versão que não modifique o arquivo original

perl -pe'
        next unless /,/; # Skip lines with no commas
        chomp;
        $csv = /(\s*[^,\s]+,.*)/;
        $new_csv = join ",",reverse split /,/,$csv;
        $new_csv .= "\n"; # The newline removed by chomp
        s/\Q$csv/$new_csv/;
    ' your_file

Suposições

  • Você não se importa com o espaçamento ao redor das vírgulas.
  • O primeiro dos valores de CSV é desviado de .BYTE em pelo menos um único espaço.
  • Ao "reverter a ordem", você quer dizer que a ordem em que eles são encontrados no arquivo não é ordenada por ordem numérica decrescente.

Entrada

ITINERARY_ARRAY_01
    .BYTE <ITINERARY_00A
    .BYTE <ITINERARY_01A
    .BYTE <ITINERARY_02A
    .BYTE <ITINERARY_03A
    .BYTE <ITINERARY_04A
    .BYTE <ITINERARY_05A
    .BYTE <ITINERARY_06A
    .BYTE <ITINERARY_07A
    .BYTE <ITINERARY_08A
    .BYTE <ITINERARY_09A
    .BYTE <ITINERARY_10A
    .BYTE <ITINERARY_11A
    .BYTE <ITINERARY_12A
    .BYTE <ITINERARY_13A
    .BYTE <ITINERARY_14A
;-------------------
ITINERARY_01E
    .BYTE $03, $05, $07, $00
;-------------------
ITINERARY_01F
    .BYTE $03, $05, $07, $09, $00
;-------------------
ITINERARY_01G
    .BYTE $28, $0D, $00
;-------------------
ITINERARY_01H
    .BYTE $28, $0D, $0F, $13, $00
;-------------------
ITINERARY_01I
    .BYTE $28, $0D, $0F, $11, $00
;-------------------
ITINERARY_01J
    .BYTE $03, $05, $07, $09, $20, $1E, $00
;-------------------
ITINERARY_01K
    .BYTE $28, $0D, $0F, $13, $15, $00
;-------------------
ITINERARY_01L
    .BYTE $03, $05, $07, $09, $20, $1E, $1C, $27
    .BYTE $00
;---------------------

Resultado

ITINERARY_ARRAY_01
    .BYTE <ITINERARY_00A
    .BYTE <ITINERARY_01A
    .BYTE <ITINERARY_02A
    .BYTE <ITINERARY_03A
    .BYTE <ITINERARY_04A
    .BYTE <ITINERARY_05A
    .BYTE <ITINERARY_06A
    .BYTE <ITINERARY_07A
    .BYTE <ITINERARY_08A
    .BYTE <ITINERARY_09A
    .BYTE <ITINERARY_10A
    .BYTE <ITINERARY_11A
    .BYTE <ITINERARY_12A
    .BYTE <ITINERARY_13A
    .BYTE <ITINERARY_14A
;-------------------
ITINERARY_01E
    .BYTE $00, $07, $05, $03
;-------------------
ITINERARY_01F
    .BYTE $00, $09, $07, $05, $03
;-------------------
ITINERARY_01G
    .BYTE $00, $0D, $28
;-------------------
ITINERARY_01H
    .BYTE $00, $13, $0F, $0D, $28
;-------------------
ITINERARY_01I
    .BYTE $00, $11, $0F, $0D, $28
;-------------------
ITINERARY_01J
    .BYTE $00, $1E, $20, $09, $07, $05, $03
;-------------------
ITINERARY_01K
    .BYTE $00, $15, $13, $0F, $0D, $28
;-------------------
ITINERARY_01L
    .BYTE $27, $1C, $1E, $20, $09, $07, $05, $03
    .BYTE $00
;---------------------
    
por 19.10.2014 / 00:20
1

Com sua entrada, você pode usar perl :

$ perl -MText::Tabs -anle '
    BEGIN {$tabstop = 4};
    print and next if /^\S/;
    @nums = grep { $_ =~ /\d+/ } @F;
    map { s/\D//g } @nums;
    map { $_ = (pop @nums) . (@nums==0 ? "" : ",")
        if $_ =~ /\d+/ } @F;
    print expand "\t@F";
' file
LABEL1
    .BYTE 05, 04, 03, 02, 01
    .BYTE 03, 02, 01

Eu suponho que sua entrada original foi classificada. Caso contrário, você pode usar @nums = sort { $a <=> $b } grep { $_ =~ /\d+/ } @F; em vez de @nums = grep { $_ =~ /\d+/ } @F; .

    
por 18.10.2014 / 10:03

Tags