lunes, 30 de mayo de 2011

Cogiendo N líneas aleatorías sin repetición de un archivo

Se me presentó el siguiente problema: tenía un archivo con N líneas y debía tomar/coger de éste M líneas no repetidas y de forma aleatoria. Siendo M < N. Como me "da nota"/mola bash, decidí hacerlo con un script. Antes explico el algoritmo, inspirado en un método para barajar cartas:

  1. Creando el mazo de cartas. Creo un arreglo con tantas posiciones como líneas tiene el archivo de entrada (el de N líneas).

  2. Barajando el mazo de cartas. Cojo el primer y último elemento del arreglo origen (source) y los coloco de últimos en el arreglo destino (tmp). Esto lo hago hasta vaciar el arreglo de origen y repito el procedimiento un número "aleatorio" de veces, entre 20 y 120 veces en este ejemplo. Siempre reemplazando al final de cada bucle el arreglo de origen por el temporal.

  3. Intercambiando última y primera carta. Esto para garantizar que la primera carta no permanezca siempre igual

  4. Sacando M cartas del mazo. Cojo las primeras M cartas del mazo.


#!/bin/bash

# Variables
fileName=$1
inputLines=`wc -l $fileName | cut -d' ' -f1`
outputLines=$2 # 596 

# Filling array
for i in `seq 0 $((inputLines-1))`; do
  source[$i]=$((i+1))
done

# Shuffling array
ceil=$((RANDOM%10*10+20)) # from 20 to 120, increment 10

for rand in `seq 1 $ceil`; do

  tmpIndex=0
  for srcIndex in `seq 0 $(($inputLines/2))`; do
    let srcTopIndex=$inputLines-$srcIndex

    tmp[$tmpIndex]=${source[$srcIndex]}
    tmp[$((tmpIndex+1))]=${source[$srcTopIndex]}

    let tmpIndex+=2

  done

  # for arrays with odd length
  if [ $(($inputLines%2)) -eq 1 ]; then
    tmp[$tmpIndex]=${source[$((srcIndex+1))]}
  fi

  source=( ${tmp[@]} ) # copying tmp array
done

# Changing first element for last one
tmp=source[0]
source[0]=${source[$((inputLines-1))]}
source[$((inputLines-1))]=$tmp

# Printing random lines of input file
for i in `seq 0 $(($outputLines-1))`; do
  sed -n "${source[$i]}p" $fileName
done

Cosas interesantes en este ejemplo:
  • Manejo de arreglos: ver iteraciones, copia, extracción y asignación de valores.
  • Extracción de una línea específica de un archivo, basado en el número de línea con sed.


No hay comentarios:

Publicar un comentario