Quiero mezclar aleatoriamente las líneas de un archivo de texto y crear un nuevo archivo. El archivo puede tener varios miles de líneas.
¿Cómo puedo hacer eso con cat, awk, cut, etc?
Quiero mezclar aleatoriamente las líneas de un archivo de texto y crear un nuevo archivo. El archivo puede tener varios miles de líneas.
¿Cómo puedo hacer eso con cat, awk, cut, etc?
Respuestas:
Puedes usar shuf. Al menos en algunos sistemas (no parece estar en POSIX).
Como señaló jleedev: sort -Rtambién podría ser una opción. En algunos sistemas al menos; Bueno, te haces una idea. Se ha señalado que sort -Rrealmente no se baraja, sino que clasifica los elementos según su valor hash.
[Nota del editor: sort -R casi se baraja, excepto que las líneas duplicadas / las claves de clasificación siempre terminan una al lado de la otra . En otras palabras: solo con líneas / teclas de entrada únicas es una verdadera combinación aleatoria. Si bien es cierto que el orden de salida está determinado por los valores hash , la aleatoriedad proviene de elegir una función hash aleatoria ; consulte el manual .]
shufy sort -Rdifieren ligeramente, porque sort -Rordena aleatoriamente los elementos según el hash de ellos, es decir, sort -Rjuntará los elementos repetidos, mientras shufbaraja todos los elementos aleatoriamente.
brew install coreutilsluego use gshuf ...(:
sort -Ry shufdebería ser visto como completamente diferente. sort -REs determinista. Si lo llama dos veces en diferentes momentos en la misma entrada, obtendrá la misma respuesta. shuf, por otro lado, produce una salida aleatoria, por lo que probablemente dará una salida diferente en la misma entrada.
Perl one-liner sería una versión simple de la solución de Maxim
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
\n; sí, eso \ndebe estar presente, y generalmente lo está , de lo contrario obtendrá lo que describe.
<STDIN>con <>, por lo que la solución también funciona con la entrada de archivos .
Esta respuesta complementa las muchas excelentes respuestas existentes de las siguientes maneras:
Las respuestas existentes se agrupan en funciones de shell flexibles :
stdinentrada, sino también argumentos de nombre de archivoSIGPIPEde la manera habitual (terminación silenciosa con código de salida 141), en lugar de romperse ruidosamente. Esto es importante cuando se canaliza la salida de la función a una tubería que se cierra temprano, como cuando se canaliza a head.Se realiza una comparación de rendimiento .
awk, sortycut , adaptada de la propia respuesta del OP :shuf() { awk 'BEGIN {srand(); OFMT="%.17f"} {print rand(), $0}' "$@" |
sort -k1,1n | cut -d ' ' -f2-; }
shuf() { perl -MList::Util=shuffle -e 'print shuffle(<>);' "$@"; }
shuf() { python -c '
import sys, random, fileinput; from signal import signal, SIGPIPE, SIG_DFL;
signal(SIGPIPE, SIG_DFL); lines=[line for line in fileinput.input()];
random.shuffle(lines); sys.stdout.write("".join(lines))
' "$@"; }
Consulte la sección inferior para obtener una versión de Windows de esta función.
shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "$@"; }
Comparación de rendimiento:
Nota: Estos números se obtuvieron en un iMac de finales de 2012 con Intel Core i5 de 3.2 GHz y una unidad Fusion, con OSX 10.10.3. Si bien los tiempos variarán con el sistema operativo utilizado, las especificaciones de la máquina, la awkimplementación utilizada (por ejemplo, la awkversión BSD utilizada en OSX suele ser más lenta que GNU awky especialmente mawk), esto debería proporcionar una sensación general de rendimiento relativo .
El archivo de entrada es un archivo de 1 millón de líneas producido con seq -f 'line %.0f' 1000000.
Los tiempos se enumeran en orden ascendente (el más rápido primero):
shuf
0.090s0.289s0.589s1.342scon Python 2.7.6; 2.407s(!) con Python 3.4.2awk+ sort+cut
3.003scon BSD awk; 2.388scon GNU awk(4.1.1); 1.811scon mawk(1.3.4);Para una mayor comparación, las soluciones no se empaquetan como las funciones anteriores:
sort -R (no es una combinación aleatoria verdadera si hay líneas de entrada duplicadas)
10.661s - asignar más memoria no parece hacer la diferencia24.229sbash bucles + sort
32.593sConclusiones :
shuf, si puede , es el más rápido con diferencia.awk+ compatible con POSIX como último recursosortcut ; el que awkla aplicación utiliza asuntos ( mawkes más rápido que GNU awk, BSD awkes más lento).sort -R, bashbucles, y Scala.Versiones de Windows de la solución Python (el código de Python es idéntico, excepto por las variaciones en las citas y la eliminación de las declaraciones relacionadas con la señal, que no son compatibles con Windows):
$OutputEncodingsi desea enviar caracteres no ASCII a través de la canalización):# Call as `shuf someFile.txt` or `Get-Content someFile.txt | shuf`
function shuf {
$Input | python -c @'
import sys, random, fileinput;
lines=[line for line in fileinput.input()];
random.shuffle(lines); sys.stdout.write(''.join(lines))
'@ $args
}
Tenga en cuenta que PowerShell puede barajar de forma nativa a través de su Get-Randomcmdlet (aunque el rendimiento puede ser un problema); p.ej:
Get-Content someFile.txt | Get-Random -Count ([int]::MaxValue)
cmd.exe(un archivo por lotes):Guardar en archivo shuf.cmd, por ejemplo:
@echo off
python -c "import sys, random, fileinput; lines=[line for line in fileinput.input()]; random.shuffle(lines); sys.stdout.write(''.join(lines))" %*
python -c "import sys, random; lines = [x for x in sys.stdin.read().splitlines()] ; random.shuffle(lines); print(\"\n\".join([line for line in lines]));"
from signal import signal, SIGPIPE, SIG_DFL; signal(SIGPIPE, SIG_DFL);de la solución original es suficiente y conserva la flexibilidad de poder también pasar argumentos de nombre de archivo ; no es necesario cambiar nada más (excepto las citas). Consulte la nueva sección que he agregado en fondo.
Utilizo un pequeño script en perl, al que llamo "sin ordenar":
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
También tengo una versión delimitada por NULL, llamada "unsort0" ... útil para usar con find -print0 y así sucesivamente.
PD: Votado como 'shuf' también, no tenía idea de que estaba allí en coreutils en estos días ... lo anterior aún puede ser útil si sus sistemas no tienen 'shuf'.
<STDIN>con <>para que la solución funcione con la entrada de archivos también.
Aquí hay un primer intento que es fácil para el codificador pero difícil para la CPU, que antepone un número aleatorio a cada línea, los ordena y luego elimina el número aleatorio de cada línea. En efecto, las líneas se ordenan al azar:
cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
head myfile | awk .... Entonces solo lo cambio a gato; por eso lo dejaron allí.
-k1 -nordenar, ya que la salida de awk rand()es un decimal entre 0 y 1 y porque lo único que importa es que se reordena de alguna manera. -k1podría ayudar a acelerarlo ignorando el resto de la línea, aunque la salida de rand () debería ser lo suficientemente única como para hacer un cortocircuito en la comparación.
cat filename |(o < filename |) que recordar cómo cada programa toma la entrada del archivo (o no).
aquí hay un script awk
awk 'BEGIN{srand() }
{ lines[++d]=$0 }
END{
while (1){
if (e==d) {break}
RANDOM = int(1 + rand() * d)
if ( RANDOM in lines ){
print lines[RANDOM]
delete lines[RANDOM]
++e
}
}
}' file
salida
$ cat file
1
2
3
4
5
6
7
8
9
10
$ ./shell.sh
7
5
10
9
6
8
2
1
3
4
awkcon sorty cut. Para no más de varios miles de líneas, no hace mucha diferencia, pero con un mayor número de líneas es importante (el umbral depende de la awkimplementación utilizada). Una ligera simplificación sería reemplazar líneas while (1){y if (e==d) {break}con while (e<d).
Una línea para python:
python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile
Y para imprimir solo una sola línea aleatoria:
python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile
Pero vea esta publicación para conocer los inconvenientes de Python random.shuffle(). No funcionará bien con muchos (más de 2080) elementos.
/dev/urandomhace. Para utilizarlo desde Python: random.SystemRandom().shuffle(L).
.readLines()devuelve las líneas con una nueva línea final.
La función simple basada en awk hará el trabajo:
shuffle() {
awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}
uso:
any_command | shuffle
Esto debería funcionar en casi cualquier UNIX. Probado en Linux, Solaris y HP-UX.
Actualizar:
Tenga en cuenta que los ceros a la izquierda ( %06d) y la rand()multiplicación hacen que funcione correctamente también en sistemas donde sortno se entienden los números. Se puede ordenar por orden lexicográfico (también conocido como comparación de cadena normal).
"$@", también funcionará con archivos como entrada. No hay razón para multiplicar rand(), porque sort -nes capaz de ordenar fracciones decimales. Sin embargo, es una buena idea controlar awkel formato de salida, ya que con el formato predeterminado %.6g, rand()generará un número ocasional en notación exponencial . Si bien barajar hasta 1 millón de líneas es suficiente en la práctica, es fácil admitir más líneas sin pagar una gran penalización de rendimiento; por ej %.17f.
sortdebería poder manejar fracciones decimales (incluso con miles de separadores, como acabo de notar).
Ruby FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
puts ARGF.readlines.shuffle, puede hacer que funcione con entrada de stdin y argumentos de nombre de archivo.
ruby -e 'puts $<.sort_by{rand}': ARGF ya es enumerable, por lo que podemos barajar las líneas clasificándolas por valores aleatorios.
Un delineador para Python basado en la respuesta de scai , pero a) toma stdin, b) hace que el resultado sea repetible con seed, c) selecciona solo 200 de todas las líneas.
$ cat file | python -c "import random, sys;
random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
> 200lines.txt
Una forma simple e intuitiva sería usar shuf.
Ejemplo:
Asumir words.txtcomo:
the
an
linux
ubuntu
life
good
breeze
Para barajar las líneas, haga:
$ shuf words.txt
que arrojaría las líneas barajadas a la salida estándar ; Entonces, debe canalizarlo a un archivo de salida como:
$ shuf words.txt > shuffled_words.txt
Una ejecución de este tipo podría producir:
breeze
the
linux
an
ubuntu
good
life
Este es un script de Python que guardé como rand.py en mi carpeta de inicio:
#!/bin/python
import sys
import random
if __name__ == '__main__':
with open(sys.argv[1], 'r') as f:
flist = f.readlines()
random.shuffle(flist)
for line in flist:
print line.strip()
En Mac OSX sort -Ry shufno están disponibles, puede alias esto en su bash_profile como:
alias shuf='python rand.py'
Si, como yo, viniste aquí para buscar una alternativa shufpara macOS, úsala randomize-lines.
Instale el randomize-linespaquete (homebrew), que tiene un rlcomando que tiene una funcionalidad similar a shuf.
brew install randomize-lines
Usage: rl [OPTION]... [FILE]...
Randomize the lines of a file (or stdin).
-c, --count=N select N lines from the file
-r, --reselect lines may be selected multiple times
-o, --output=FILE
send output to file
-d, --delimiter=DELIM
specify line delimiter (one character)
-0, --null set line delimiter to null character
(useful with find -print0)
-n, --line-number
print line number with output lines
-q, --quiet, --silent
do not output any errors or warnings
-h, --help display this help and exit
-V, --version output version information and exit
brew install coreutilsproporciona el shufbinario como gshuf.
Si tiene instalado Scala, aquí hay una línea para mezclar la entrada:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
Esta función bash tiene una dependencia mínima (solo sort y bash):
shuf() {
while read -r x;do
echo $RANDOM$'\x1f'$x
done | sort |
while IFS=$'\x1f' read -r x y;do
echo $y
done
}
awksolución asistida por el OP , pero el rendimiento será un problema con una entrada más grande; el uso de un solo $RANDOMvalor baraja correctamente solo hasta 32.768 líneas de entrada; Si bien podría extender ese rango, probablemente no valga la pena: por ejemplo, en mi máquina, ejecutar su script en 32.768 líneas de entrada cortas toma aproximadamente 1 segundo, que es aproximadamente 150 veces más que la ejecución shuf, y aproximadamente 10-15 veces mientras dure la awksolución asistida por el OP . Si puede confiar en sortestar presente, también awkdebería estar allí.
En Windows, puede probar este archivo por lotes para ayudarlo a barajar sus datos.txt. El uso del código de lote es
C:\> type list.txt | shuffle.bat > maclist_temp.txt
Después de emitir este comando, maclist_temp.txt contendrá una lista aleatoria de líneas.
Espero que esto ayude.
No mencionado hasta el momento:
El unsortutil. Sintaxis (algo orientada a la lista de reproducción):
unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
[--identity] [--filenames[=profile]] [--separator sep] [--concatenate]
[--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null]
[--linefeed] [file ...]msort puede barajarse por línea, pero generalmente es exagerado:
seq 10 | msort -jq -b -l -n 1 -c r