Classificar arquivos de texto com várias linhas como uma linha

12

Eu tenho um arquivo de texto neste formato:

####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Eu quero classificar este arquivo por KEY line e manter as próximas 4 linhas com ele no resultado, então o resultado classificado deve ser:

####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Existe uma maneira de fazer isso?

    
por RYN 31.12.2016 / 13:09

5 respostas

11
O

msort(1) foi projetado para classificar arquivos com registros de várias linhas. Ele tem um gui opcional, bem como uma versão de linha de comando normal e utilizável para humanos. (Pelo menos, humanos que gostam de ler atentamente os manuais e procurar exemplos ...)

AFAICT, você não pode usar um padrão arbitrário para registros, portanto, a menos que seus registros tenham tamanho fixo (em bytes, não em caracteres ou linhas). msort tem uma opção -b para registros que são blocos de linhas separadas por linhas em branco.

Você pode transformar sua entrada em um formato que funcionará com -b facilmente, colocando uma linha em branco antes de cada ###... (exceto a primeira).

Por padrão, ele imprime estatísticas no stderr, então, pelo menos, é fácil dizer quando ele não ordenou, porque achava que a entrada inteira era um único registro.

msort funciona em seus dados. O comando sed preenche uma nova linha para cada linha #+ , exceto para a linha 1. -w classifica o registro inteiro (lexicograficamente). Há opções para escolher qual parte de um registro usar como chave, mas eu não precisava delas.

Também deixei de tirar as novas linhas extras.

$ sed '2,$ s/^#\+/\n&/' unsorted.records | msort -b -w 2>/dev/null 
####################################
KEY1
VAL11
VAL12
VAL13
VAL14

####################################
KEY2
VAL21
VAL22
VAL23
VAL24

####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Não tive sorte com -r '#' para usar isso como separador de registro. Achava que o arquivo inteiro era um registro.

    
por 31.12.2016 / 17:06
8

Uma solução é primeiro alterar os feeds de linha dentro de um bloco para um caractere não utilizado de sua escolha ('|' no exemplo abaixo), para classificar o resultado e alterar o separador escolhido para o feed de linha original:

sed -e 'N; N; N; N; N; s/\n/|/g' file.txt \
| sort -k2,2 -t\| \
| sed 's/|/\n/g'
    
por 31.12.2016 / 13:24
4
perl -0ne 'print sort /(#+[^#]*)/g' file.txt
  • perl -0 usa o arquivo inteiro
  • /(....)/g corresponde e extrai os registros
  • print sort ... sort e imprimi-los
por 31.12.2016 / 21:27
2

Aqui está outra maneira que deve funcionar com qualquer número de linhas em uma seção KEY :

# extract delimiter
delim=$(head -n1 <infile)
sed '/#/d;/KEY/h;G;s/\n/\x02/' infile | nl -ba -nrz -s $'
# extract delimiter
delim=$(head -n1 <infile)
sed '/#/d;/KEY/h;G;s/\n/\x02/' infile | nl -ba -nrz -s $'%pre%2' | sort -t $'%pre%2' -k3 -k1,1 |
cut -d $'%pre%2' -f2 | sed '/KEY/{x;s/.*/'"${delim}"'/;G}'
2' | sort -t $'%pre%2' -k3 -k1,1 | cut -d $'%pre%2' -f2 | sed '/KEY/{x;s/.*/'"${delim}"'/;G}'

Isso funciona salvando o delimitador em uma variável (para removê-lo da entrada). Em seguida, ele anexa KEY* a cada linha em sua seção correspondente usando um caracter baixo ascii (que provavelmente não ocorre em sua entrada) como separador e, em seguida, n umbers all l ines usando o mesmo separador. É então apenas uma questão de sort ing pelo 3º e 1º campo e cut da coluna do meio e então a restauração dos delimitadores através de uma% finalsed. Observe que, acima, KEY12 classificará antes de KEY2 , portanto, ajuste o comando sort de acordo com suas necessidades.

    
por 31.12.2016 / 16:54
2

Você pode usar a biblioteca stdlib do POSIX Awk:

#!/usr/local/bin/awklib -f
$0 ~ "#" {x++}
{q[x] = q[x] ? q[x] RS $0 : $0}
END {
  arr_sort(q)
  for (x in q) print q[x]
}
    
por 01.01.2017 / 06:46