você está aqui: Home  → Arquivo de Mensagens

Script para embaralhar aleatoriamente as linhas de um arquivo

Colaboração: Júlio Cezar Neves

Data de Publicação: 25 de setembro de 2017

O script de hoje ilustra uma forma de se embaralhar, de forma aleatória, as linhas de um arquivo.

1. Criação do Arquivo de teste

$ seq -f "Numero %g" 10 > entrada.txt    # Criando arq de teste
$ cat entrada.txt
Numero 1
Numero 2
Numero 3
Numero 4
Numero 5
Numero 6
Numero 7
Numero 8
Numero 9
Numero 10

2. Criação do Script mix.sh

$ cat mix.sh
#!/bin/sh

# Transforma o IFS em somente 
IFS="
"
NumReg=$(cat entrada.txt | wc -l) # Total de Registros

i=0
# j vai variar de 0 até NumReg-1
j=$((RANDOM % $NumReg))
for Reg in $(cat entrada.txt)
do
# Enquanto o array de saida (aSai) tiver cheio...

    while [ ${aSai[$j]} ]
    do
        j=$((RANDOM % $NumReg))
    done
    aSai[$j]="$Reg"   # Move registro para posição randomica no array done
> saida.txt           # Cria o arquivo de saida vazio

# Mais um tipo de for :)

for ((i=0; i<=$NumReg-1; i++))
do
    echo ${aSai[i]} >> saida.txt  # Carrega aleatóriamente o arquivo de saida
done


3. Execução do script

$ ./mix.sh
$ cat saida.txt  # Primeira tentativa
Numero 8
Numero 2
Numero 3
Numero 1
Numero 5
Numero 10
Numero 7
Numero 9
Numero 6
Numero 4

$ ./mix.sh
$ cat saida.txt  # Segunda tentativa
Numero 5
Numero 9
Numero 7
Numero 8
Numero 1
Numero 3
Numero 2
Numero 10
Numero 4
Numero 6

Um script com esta funcionalidade pode ter várias aplicações. Por exemplo, em um website, se queremos exibir em determinada página uma lista de links, em que desejamos rotacionar os links que aparecem no topo da página, podemos usa-lo para que a ordem seja sempre trocada. O script pode ser acionado via cron, de 10 em 10 minutos, por exemplo.



Veja a relação completa dos artigos de Júlio Cezar Neves

 

 

Opinião dos Leitores

Arnaldo Mandel
27 Set 2017, 15:37
Gustavo, seu comentários são muito pertinentes e vou mencionar dois deles:

1) Uso inútil do cat - isso parece uma batalha perdida, tem gente que nunca vai acitar isso. Eu já comentei muito isso, até em outros posts da dicas-L, mas não tem jeito. Na prática não faz muita diferença, e é uma questão de elegância, que é em parte questão de gosto.

2) Seu script. Ele é uma implementação muito elegante do algoritmo de Fisher-Yates. Na formulação original, ele gera a permutação aleatória no lugar, por meio de trocas; na sua implementação, em vez de troca, um elemento vai embora e o outro toma o lugar dele. Essa mudança sutil e econômica é que mereceu o adjetivo "elegante".
Gustavo Chaves
27 Set 2017, 14:00
Eu ia sugerir o comando shuf como alternativa ao script mas o dr. Arnaldo Mandel foi mais rápido... :-)

Não há mesmo problema em reimplementar um comando existente de modo simples com objetivos didáticos. Ainda assim, há várias oportunidades de melhoria no script que poderiam torná-lo um material didático mais interessante. Por exemplo:

- A primeira linha do script invoca a /bin/sh mas devia invocar /bin/bash, porque a /bin/sh não suporta vetores. Pelo menos não no meu Ubuntu 16.04.

- Falta um "done" pra fechar o primeiro "for".

- De início o script já ganha um prêmio por "Uso Inútil do Cat" (http://porkmail.org/era/unix/award.html) fazendo "cat entrada.txt | wc -l" quando poderia fazer mais simplesmente "wc -l entrada.txt".

- A entrada e a saída do script são arquivos com nome fixo (entrada.txt e saida.txt). Scripts como esse são melhor implementados como filtros, lendo da entrada padrão e gerando o resultado na saída padrão, pra torná-los mais genéricos.

- O script lê o arquivo duas vezes. Na primeira apenas pra contar o número de linhas do arquivo. Mas há como evitar isso e lê-lo apenas uma vez. O truque é inserir as linhas sequencialmente no vetor e deixar pra escrevê-las aleatoriamente depois.

- As linhas são atribuídas ao vetor aSai aleatoriamente. O índice de cada linha é determinado gerando índices aleatoriamente até encontrar um vazio. Mas a chance de encontrar um índice vazio vai ficando cada vez menor quanto mais cheio o vetor vai ficando. Imagine ler um arquivo de 1000 linhas. Depois de ler as primeiras 999 o script pode perder muito tempo ter a "sorte" de encontrar o índice vazio. Rodei o mix.sh pra um arquivo de 10000 linhas e ele demorou por volta de 8 segundos pra executar. A versão alternativa que implementei demorou 0,6 segundos, consistentemente.

Fiz uma versão alternativa do script, procurando deixá-lo mais eficiente e didático. Veja em: https://gist.github.com/gnustavo/f402cdbacbb4cddf01e80fa49593f31f
Arnaldo Mandel
26 Set 2017, 14:23
Parece que você se ofendeu, em vez de entender a crítica. Por exemplo: Show me the code. Ora, a primeira palavra da minha mensagem foi shuf . Isso é o nome de um programa GNU (portamto, com código aberto, fácil de achar) que faz o que o seu script faz, sem os problemas dele.

Talvez embaralhar as linhas de um arquivo sirva prá algo, mas não para rotacionar o dito.

Finalmente, se seu objetivo era mostrar o uso de vetor no shell e você tivesse dito isso, eu teria encarado o script como exemplo didático e se tivesse criticado seria de outra forma.
julio Neves
26 Set 2017, 09:09
Arnaldo, vc tem toda razão, mas essa me desculpe por dar uma dica tão básica para um cara que é PhD em Shell como vc parece ser.

Sabe o que passou na minha cabeça? Ajudar a cerca de 90% dos usuários linux que nem sabem que o Shell trabalha com vetores.

Mas aí vai uma recomendação final de um cara que está ativamente engajado no movimento de SwL há mais de 20 anos: se vc escrever um artigo melhor que esse tenho certeza que a Dicas-l publicará e desta forma ajudará muito mais às pessoas que simplesmente criticar. Isso não é um desafio é somente um apelo que faço para que todos possamos desfrutar o seu conhecimento.

Alias, uma das melhores frases que já ouvi foi do Linus: Talk is easy. Show me the code
Arnaldo Mandel
26 Set 2017, 07:50
shuf

Para que usar um script amador se existe pronto um código (livre, aberto) profissional?

Se o arquivo for gigante e não couber na memória, esse script vai pro buraco.

E a aplicação sugerida é ridícula: para rotacionar uma lista, não tem porque embaralhar o arquivo.
*Nome:
Email:
Me notifique sobre novos comentários nessa página
Oculte meu email
*Texto:
 
  Para publicar seu comentário, digite o código contido na imagem acima
 


Powered by Scriptsmill Comments Script