Como ordenar blocos de linhas com uma linha específica do bloco?

1

Eu tenho um arquivo com os dados como este

BEGIN
hello2
5
world1
END
BEGIN
hello4
2
world5
END
BEGIN
hello6
4
END

Eu quero classificar as linhas da seguinte maneira com base no número dentro do bloco. Os números são isolados e únicos.

BEGIN
hello4
2
world5
END
BEGIN
hello6
4
END
BEGIN
hello2
5
world1
END

Eu sei imprimir os blocos com sed e awk. Isso é sobre isso.

    # Prints the blocks including the BEGIN and END tags
    cat file | sed -n '/^BEGIN$/,/^END$/p'

    # Prints the blocks exluding the BEGIN and END tags
    awk '/^BEGIN$/ {show=1;next} /^END$/{show=0}  { print }' file
    
por navmad 08.12.2015 / 19:29

3 respostas

1

Sempre que uma linha BEGIN for encontrada, leia separadamente a próxima linha numérica do arquivo usando um identificador separado por meio de getline . Imprima cada linha do arquivo com dois prefixos, o valor numérico que foi recuperado anteriormente e o número do registro do arquivo do registro atual (assim todas as linhas dentro do mesmo bloco BEGIN .. END terminam com o mesmo valor no prefixo 1 correspondente ao número incorporado no bloco). Alimente isso com os utilitários externos sort e cut para manipular a classificação baseada em prefixo, descartando os prefixos.

awk '/BEGIN/{"awk \$0+0==\$0 "FILENAME | getline x}
{print x"~"FNR"~"$0 | "sort -k1,1n -k2,2n -t~ | cut -f3- -d~"}' file
BEGIN
hello4
2
world5
END
BEGIN
hello6
4
END
BEGIN
hello2
5
world1
END
    
por 08.12.2015 / 21:30
2

Usando o GNU awk:

gawk '
    BEGIN { RS="\nEND\n"; ORS = RS; FS = "\n" }
    { record[$3] = $0 }
    END {
        PROCINFO["sorted_in"] = "@ind_num_asc"
        for (val in record) print record[val]
    }
' file

Com base nos seus dados, suponho que sempre haja uma linha entre o BEGIN e o número.

A linha PROCINFO define como a matriz "registros" é percorrida. Consulte o link

    
por 08.12.2015 / 21:16
1

A primeira linha agrega o bloco de texto, linha por linha, e também tenta encontrar um número para usar como uma avaliação de classificação depois. A cláusula if if($0+0==$0) avalia true quando encontra um número.

O segundo bloco é executado quando encontra um "END" na entrada, então salva o bloco em uma matriz associativa, indexando-o usando o número encontrado no bloco.

awk '{block=block"\n"$0; if($0+0==$0) num=$0;} 
/^END$/ {blks[num]=block; block=""} 
END {for(key in blks) print blks[key]}' file

A última linha é apenas imprimir todas as entradas da matriz quando ela atinge o final do arquivo de entrada. Note que o array associativo já está classificado (é assim que funciona internamente), então só precisamos iterar sobre ele imprimindo todas as entradas.

Por exemplo, veja o seguinte script awk :

echo | awk '{a[2]="b"; a[1]="a"; a[3]="c"; for(key in a) print a[key];}'

Produz:

a
b
c

Na minha resposta, estou imprimindo um \n extra antes de cada bloco, suponho que isso não seja um problema. A saída para o seu exemplo é:

BEGIN
hello4
2
world5
END

BEGIN
hello6
4
END

BEGIN
hello2
5
world1
END

Se você não quiser a linha extra, substitua o primeiro bloco do meu script awk por:

{if(length(block)=="0")block=$0; else{block=block"\n"$0; if($0+0==$0) num=$0}}

Aqui está a versão de uma linha:

awk '{if(length(block)=="0")block=$0; else{block=block"\n"$0; if($0+0==$0) num=$0}} /^END$/ {blks[num]=block; block=""} END {for(key in blks) print blks[key]}' file
    
por 08.12.2015 / 20:49