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

13

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 KEYlinha e manter as próximas 4 linhas com ele no resultado para que o resultado classificado seja:

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

existe uma maneira de fazer isso?

RYN
fonte
5
não atravesse post por favor
Zanna
@ Zanna: Eu acho que há uma exclusão para as seções unix e askubuntu, pois esses dois se sobrepõem muito! Eu acho que li sobre isso na seção meta do unix
RYN
2
meta questão relevante feita aqui pelo AU mod :) Como devem ser tratadas as questões postadas no Ask Ubuntu?
Zanna
@RYN O problema não seria a sobreposição, na verdade muitos sites de SE se sobrepõem, mas as pessoas que dão respostas podem não saber sobre as respostas no outro site.
Phd

Respostas:

12

msort(1)foi projetado para poder classificar arquivos com registros de várias linhas. Ele possui uma interface gráfica opcional, além de uma versão normal e utilizável para humanos. (Pelo menos, seres humanos que gostam de ler os manuais com atenção 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 caracteres ou linhas). msortpossui uma -bopção para registros que são blocos de linhas separados por linhas em branco.

Você pode transformar sua entrada em um formato que funcione com -bbastante facilidade, colocando uma linha em branco antes de cada ###...(exceto a primeira).

Por padrão, ele imprime estatísticas no stderr, pelo menos é fácil saber quando não classificou, porque achou que toda a entrada era um único registro.


msort funciona em seus dados. O sedcomando anexa uma nova linha a cada #+linha, exceto a linha 1. -wclassifica o registro inteiro (lexicograficamente). Existem opções para escolher qual parte de um registro usar como chave, mas eu não precisava delas.

Também deixei de fora 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 -r '#'em usar isso como separador de registros. Ele achava que o arquivo inteiro era um registro.

Peter Cordes
fonte
Muito obrigado; msorté muito útil; graças (cerca -rparece que é porque há mais de um # eu usei -de funcionou
RYN
legal! (+1) msort -qwr '#' ex funciona para mim (bem chages o rec saída separador.)
JJoao
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), classificar o resultado e alterar novamente 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'
xhienne
fonte
1
Obrigado; isso funciona, mas está muito sujo, especialmente quando os dados também estão sujos! se as linhas após a chave eram 100, preciso colocar 100 ;Nlá, e pode ser difícil encontrar um caractere que não seja usado no próprio texto; é muito bom para , sortou awk... ser capaz de fazer a triagem de
múltiplas linhas
4
perl -0ne 'print sort /(#+[^#]*)/g' file.txt
  • perl -0 sorve o arquivo inteiro
  • /(....)/g combinar e extrair os registros
  • print sort ... classifique e imprima-os
JJoao
fonte
2

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

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

Isso funciona salvando o delimitador em uma variável (para removê-lo da entrada). Em seguida, anexa a KEY*cada linha em sua seção correspondente usando um caractere ASCII baixo (o que é improvável que ocorra em sua entrada) como um separador e depois nsepara todas as lcolunas usando o mesmo separador. É apenas uma questão de sortentrar no terceiro e no primeiro campos e cutcolocar a coluna do meio e restaurar os delimitadores por meio de uma final sed. Observe que, com o exposto acima, KEY12a classificação será feita antes; KEY2portanto, ajuste o sortcomando de acordo com suas necessidades.

don_crissti
fonte
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]
}
Steven Penny
fonte