Classificação agrupada de parágrafos contínuos (separados por linhas em branco)?

8

Acho que agora tenho bastante experiência na classificação por colunas ; no entanto, ainda não encontrei nada até agora sobre como classificar linhas contínuas .

Supondo que tenhamos um arquivo de texto parecido com este: (muito simplificado, é claro)

Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

Agora, é possível classificar as linhas alfanumericamente por cada bloco separadamente ? Quero dizer, para que o resultado fique assim:

Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Contando com o que encontrei na sortpágina de manual, isso pode não ser possível com o sortcomando UNIX interno. Ou isso pode ser feito sem ter que recorrer a ferramentas externas / de terceiros?

erro de sintaxe
fonte

Respostas:

9

A awksolução de Drav é boa, mas isso significa executar um sortcomando por parágrafo. Para evitar isso, você pode fazer:

< file awk -v n=0 '!NF{n++};{print n,$0}' | sort -k1n -k2 | cut -d' ' -f2-

Ou você pode fazer a coisa toda em perl:

perl -ne 'if (/\S/){push@l,$_}else{print sort@l if@l;@l=();print}
          END{print sort @l if @l}' < file

Observe que acima, separadores são linhas em branco (para o awkprimeiro, linhas com apenas caracteres de espaço ou tabulação, para o perlprimeiro, qualquer caractere de espaçamento horizontal ou vertical) em vez de linhas vazias. Se você deseja linhas vazias, pode substituir !NFpor !lengthou $0=="", e /\S/por /./.

Stéphane Chazelas
fonte
Agradeço também a você, especialmente pela awksolução que evita a sortsobrecarga! Sorrateiro!
Syntaxerror 14/08
9
awk -v RS= -v cmd=sort '{print | cmd; close(cmd); print ""}' file

Definir o separador de registros RScomo uma sequência vazia faz com que awk avance nos parágrafos de cada vez. Para cada parágrafo, canalize o parágrafo (in $0) para cmd (que está definido como sort) e imprima a saída. Imprima uma linha em branco para separar os parágrafos de saída com a print "".

Se estamos dando exemplos de perl, apresento uma abordagem alternativa à de Stephane:

perl -e 'undef $/; print join "\n", sort (split /\n/), "\n" 
    foreach(split(/\n\n/, <>))' < file

Desative o separador de campos ( undef $/), isso nos permite usar <>e obter todo o STDIN. Nós, então, splitpor aí \n\n(parágrafos). foreach"paragraph", sortas linhas splittocando novas linhas, introduzindo- sortas e joinjuntando-as novamente e seguindo uma trilha \n.

No entanto, isso tem um efeito colateral de adicionar um separador "parágrafo final" no último parágrafo (se ele não tivesse um antes). Você pode contornar isso com o pouco menos bonito:

perl -e 'undef $/; print join "\n", sort (split /\n/) , (\$_ == \$list[-1] ? "" : "\n")
    foreach(@list = split(/\n\n/, <>))' < file

Isso atribui os parágrafos a @liste, em seguida, há uma "operação ternária" para verificar se é o último elemento da foreach(a \$_ == \$list[-1]verificação). imprima ""se for ( ? ...), else ( : ...) imprima "\n"para todos os outros "parágrafos" (elementos de @list).

Drav Sloan
fonte
Isso é legal! Obrigado. Você está realmente chamando /usr/bin/sortcom essa linha ou é um awkcomando interno de "classificação"?
Syntaxerror
Invocar o comando tipo, daí a necessidade de fechar (cmd) em cada ciclo :)
Drav Sloan
5

Eu escrevi uma ferramenta em haskell que permite que você use sort, shuf, tac ou qualquer outro comando em parágrafos de texto.

https://gist.github.com/siers/01306a361c22f2de0122
EDIT: a ferramenta também está incluída neste repositório: https://github.com/siers/haskell-import-sort

Ele divide o texto em blocos, une os sub-blocos com \0char, canaliza o comando e finalmente faz a mesma coisa ao contrário.

28-08-2015 : Encontrei outro uso pessoal para esta ferramenta - selecionando N parágrafos após uma linha.

paramap grep -aA2 '^reddit usernames' < ~/my-username-file
reddit usernames

foo
bar
baz

a couple
more of these
Raitis Veinbahs
fonte
4

Se você tiver o GNU awk disponível, poderá classificar cada bloco usando a asort()função interna. Algo assim:

blocksort.awk

function sort_n_print(array) {
  asort(array)
  for(i=1; i<=length(array); i++)
    print array[i]
  delete array
}

NF { a[++x] = $0 }

!NF { sort_n_print(a); print }

END { sort_n_print(a) }

Execute-o assim:

awk -f blocksort.awk infile
Thor
fonte
1

TXR Lisp passo a passo:

$ cat data
Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

$ txr -p '(get-lines)' < data
("Echo" "Alpha" "Delta" "Charlie" "" "Golf" "Bravo" "Hotel" "Foxtrot")

$ txr -t '(get-lines)' < data
Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

$ txr -p '(partition* (get-lines) (op where [chain length zerop]))' < data
(("Echo" "Alpha" "Delta" "Charlie") ("Golf" "Bravo" "Hotel" "Foxtrot"))

$ txr -p '[mapcar sort (partition* (get-lines) (op where [chain length zerop]))]' < data
(("Alpha" "Charlie" "Delta" "Echo") ("Bravo" "Foxtrot" "Golf" "Hotel"))

$ txr -p '(interpose (list "") [mapcar sort (partition* (get-lines) (op where [chain length zerop]))])' < data
(("Alpha" "Charlie" "Delta" "Echo") ("") ("Bravo" "Foxtrot" "Golf" "Hotel"))

$ txr -t '(interpose (list "") [mapcar sort (partition* (get-lines) (op where [chain length zerop]))])' < data
Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Referências: get-lines , partition * , op , where , chain , length , zerop , mapcar , interpose .

Kaz
fonte
Note que no [mapcar sort ...]nós poderíamos substituir sortpor uma função que canaliza as cordas através de um processo externo. Podemos então acabar com uma ferramenta para distribuir um comando externo de processamento de texto por parágrafos.
Kaz