Reprodução aleatória de arquivos de várias linhas

5

Eu tenho um arquivo de texto com linhas vazias separando blocos de texto. Eu gostaria de usar as ferramentas de linha de comando * NIX para embaralhar esse arquivo, respeitando a estrutura de bloco. Em outras palavras, na saída, gostaria de ver a ordem alterada de blocos; as linhas e sua ordem dentro do bloco permanecem as mesmas.

Exemplo de arquivo de entrada:

line 1
line 2

line 10
line 20
line 30

line 100
line 200

O arquivo de saída (após o shuffle):

line 10
line 20
line 30

line 1
line 2

line 100
line 200

É claro que executar repetidamente deve dar uma ordem diferente de blocos.

A primeira linha do arquivo é sempre não vazia. Não há linhas em branco duplas. A última linha do arquivo está sempre vazia.

Eu escrevi um script Python muito simples que lê todas as linhas em uma lista de listas, embaralha e sai. Estou curioso para saber se posso fazer isso com as ferramentas padrão * NIX.

    
por Vladislavs Dovgalecs 22.11.2017 / 20:33

3 respostas

8

POSIXly, você poderia fazer algo como:

<file awk '
  BEGIN{srand(); n=rand()}
  {print n, NR, $0}
  !NF {n=rand()}
  END {if (NF) print n, NR+1, ""}' |
  sort -nk1 -k2 |
  cut -d' ' -f3-

Ou seja, prefixe cada linha com <a-random-number-that-changes-with-each-paragraph> , em seguida, o número da linha, em seguida, classifique numericamente no primeiro número e depois em segundo para manter a ordem da linha nos parágrafos e remover esses números extras.

Pode-se querer canalizar para sed '$d' para remover a linha em branco final.

Tenha em atenção que com a maioria awk implementations srand() utiliza o tempo unix epoch para propagar o gerador de números pseudo-aleatórios, para que possa obter o mesmo resultado se for executado duas vezes no mesmo segundo (a erro histórico agora gravado na especificação POSIX, apesar dos meus esforços, infelizmente ).

    
por 22.11.2017 / 23:11
5

Usando as ferramentas GNU, isso divide os parágrafos em grupos separados por NUL, embaralha-os e depois remove os NULs:

$ sed '1s/^/\n/; s/^$/\x00/' input | shuf -z | sed '1d; s/\x00//'
line 100
line 200

line 10
line 20
line 30

line 1
line 2

Abordagem alternativa não usando NUL

Como nem todas as ferramentas suportam caracteres NUL, aqui está uma alternativa. Isso lê parágrafos, substitui ~ por novas linhas, depois embaralha e depois converte ~ em novas linhas antes de exibir os resultados:

$ awk '{gsub(/\n/, "~")} 1' RS= input | shuf | awk '{gsub(/~/, "\n")} 1' ORS="\n\n"
line 10
line 20
line 30

line 100
line 200

line 1
line 2

Se o seu texto puder conter ~ , use outro caractere que o texto não conterá como separador de linha temporário.

    
por 22.11.2017 / 20:54
5

Usando o perl:

perl -MList::Util -00 -e 'chomp(my @a=<>); print join("\n\n", List::Util::shuffle @a) . "\n";' < input

Ou espalhe-se como um arquivo de script:

#!/usr/bin/perl
use List::Util 'shuffle';
local $/ = "";  ## paragraph mode
chomp(my @a = <>);
print join("\n\n", shuffle @a) . "\n";
    
por 22.11.2017 / 21:02