Je souhaite mélanger les lignes d'un fichier texte de manière aléatoire et créer un nouveau fichier. Le fichier peut contenir plusieurs milliers de lignes.
Comment puis-je faire ça avec cat
, awk
, cut
, etc?
Je souhaite mélanger les lignes d'un fichier texte de manière aléatoire et créer un nouveau fichier. Le fichier peut contenir plusieurs milliers de lignes.
Comment puis-je faire ça avec cat
, awk
, cut
, etc?
Réponses:
Vous pouvez utiliser shuf
. Sur certains systèmes au moins (ne semble pas être dans POSIX).
Comme l'a souligné jleedev: sort -R
pourrait également être une option. Sur certains systèmes au moins; eh bien, vous obtenez l'image. Il a été souligné quesort -R
ne mélange pas vraiment, mais trie les éléments en fonction de leur valeur de hachage.
[Note de l'éditeur: sort -R
presque aléatoire, sauf que les lignes / clés de tri en double finissent toujours côte à côte . En d'autres termes: c'est uniquement avec des lignes / touches d'entrée uniques que c'est un véritable shuffle. S'il est vrai que l'ordre de sortie est déterminé par les valeurs de hachage , le caractère aléatoire vient du choix d'une fonction de hachage aléatoire - voir le manuel .]
shuf
et sort -R
diffèrent légèrement, car sort -R
ordonne au hasard les éléments en fonction de leur hachage , ce sort -R
qui signifie que les éléments répétés seront assemblés, tout en shuf
mélangeant tous les éléments de manière aléatoire.
brew install coreutils
gshuf ...
sort -R
et shuf
doit être considéré comme complètement différent. sort -R
est déterministe. Si vous l'appelez deux fois à des moments différents sur la même entrée, vous obtiendrez la même réponse. shuf
, d'autre part, produit une sortie aléatoire, il donnera donc très probablement une sortie différente sur la même entrée.
Perl one-liner serait une version simple de la solution de Maxim
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
\n
; oui, ce \n
doit être présent - et généralement est - sinon vous obtiendrez ce que vous décrivez.
<STDIN>
par <>
, de sorte que la solution fonctionne également avec l'entrée des fichiers .
Cette réponse complète les nombreuses excellentes réponses existantes des manières suivantes:
Les réponses existantes sont regroupées dans des fonctions shell flexibles :
stdin
entrées, mais également des arguments de nom de fichierSIGPIPE
de la manière habituelle (terminaison silencieuse avec code de sortie 141
), par opposition à une interruption bruyante. Ceci est important lors de la canalisation de la sortie de fonction vers une canalisation fermée tôt, comme lors de la canalisation vers head
.Une comparaison des performances est effectuée.
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))
' "$@"; }
Voir la section inférieure pour une version Windows de cette fonction.
shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "$@"; }
Comparaison des performances:
Remarque: Ces chiffres ont été obtenus sur un iMac fin 2012 avec Intel Core i5 à 3,2 GHz et un Fusion Drive, exécutant OSX 10.10.3. Bien que les délais varient en fonction du système d'exploitation utilisé, des spécifications de la machine et de l' awk
implémentation utilisée (par exemple, la awk
version BSD utilisée sur OSX est généralement plus lente que GNU awk
et surtout mawk
), cela devrait fournir un sentiment général de performances relatives. .
Le fichier d' entrée est un fichier de 1 million de lignes produit avec seq -f 'line %.0f' 1000000
.
Les temps sont répertoriés par ordre croissant (le plus rapide en premier):
shuf
0.090s
0.289s
0.589s
1.342s
avec Python 2.7.6; 2.407s
(!) avec Python 3.4.2awk
+ sort
+cut
3.003s
avec BSD awk
; 2.388s
avec GNU awk
(4.1.1); 1.811s
avec mawk
(1.3.4);Pour plus de comparaison, les solutions non packagées comme les fonctions ci-dessus:
sort -R
(pas un vrai shuffle s'il y a des lignes d'entrée en double)
10.661s
- allouer plus de mémoire ne semble pas faire de différence24.229s
bash
boucles + sort
32.593s
Conclusions :
shuf
, si vous le pouvez - c'est de loin le plus rapide.awk
+ compatible POSIX en dernier recourssort
cut
; l' awk
implémentation que vous utilisez mawk
est plus rapide que GNU awk
, BSDawk
est la plus lente).sort -R
, bash
boucles et Scala.Versions Windows de la solution Python (le code Python est identique, sauf pour les variations de citation et la suppression des instructions liées au signal, qui ne sont pas prises en charge sur Windows):
$OutputEncoding
si vous souhaitez envoyer des caractères non ASCII via le pipeline):# 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
}
Notez que PowerShell peut mélanger nativement via sa Get-Random
cmdlet (bien que les performances puissent être un problème); par exemple:
Get-Content someFile.txt | Get-Random -Count ([int]::MaxValue)
cmd.exe
(un fichier batch):Enregistrer dans un fichier shuf.cmd
, par exemple:
@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 solution d'origine est suffisant et conserve la flexibilité de pouvoir également passer des arguments de nom de fichier - pas besoin de changer quoi que ce soit d'autre (sauf pour les citations) - veuillez consulter la nouvelle section que j'ai ajoutée à la bas.
J'utilise un petit script perl, que j'appelle "unsort":
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
J'ai également une version délimitée par NULL, appelée "unsort0" ... pratique pour une utilisation avec find -print0 et ainsi de suite.
PS: A également voté "shuf", je ne savais pas que c'était là dans coreutils ces jours-ci ... ce qui précède peut toujours être utile si vos systèmes n'ont pas "shuf".
<STDIN>
par <>
afin de faire fonctionner la solution avec une entrée à partir de fichiers également.
Voici un premier essai facile sur le codeur mais difficile sur le processeur qui ajoute un nombre aléatoire à chaque ligne, les trie puis supprime le nombre aléatoire de chaque ligne. En effet, les lignes sont triées aléatoirement:
cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
head myfile | awk ...
. Ensuite, je le change juste en chat; c'est pourquoi il a été laissé là.
-k1 -n
de tri, car la sortie de awk rand()
est une décimale entre 0 et 1 et parce que tout ce qui compte, c'est qu'il soit réorganisé d'une manière ou d'une autre. -k1
pourrait aider à l'accélérer en ignorant le reste de la ligne, bien que la sortie de rand () devrait être suffisamment unique pour court-circuiter la comparaison.
cat filename |
(ou < filename |
) que de se rappeler comment chaque programme prend l'entrée de fichier (ou non).
voici 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
production
$ cat file
1
2
3
4
5
6
7
8
9
10
$ ./shell.sh
7
5
10
9
6
8
2
1
3
4
awk
avec sort
et cut
. Pour pas plus de plusieurs milliers de lignes, cela ne fait pas beaucoup de différence, mais avec un nombre de lignes plus élevé, cela importe (le seuil dépend de l' awk
implémentation utilisée). Une légère simplification serait de remplacer les lignes while (1){
et if (e==d) {break}
par while (e<d)
.
Un one-liner pour python:
python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile
Et pour imprimer une seule ligne aléatoire:
python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile
Mais voyez ce post pour les inconvénients de python random.shuffle()
. Cela ne fonctionnera pas bien avec de nombreux éléments (plus de 2080).
/dev/urandom
fait. Pour l' utiliser à partir de Python: random.SystemRandom().shuffle(L)
.
.readLines()
renvoie les lignes avec une nouvelle ligne de fin.
Une fonction simple basée sur awk fera le travail:
shuffle() {
awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}
usage:
any_command | shuffle
Cela devrait fonctionner sur presque tous les UNIX. Testé sur Linux, Solaris et HP-UX.
Mettre à jour:
Notez que le début des zéros ( %06d
) et la rand()
multiplication le font fonctionner correctement également sur les systèmes où sort
ne comprend pas les nombres. Il peut être trié par ordre lexicographique (alias comparaison de chaîne normale).
"$@"
, cela fonctionnera également avec les fichiers en entrée. Il n'y a aucune raison de multiplier rand()
, car sort -n
est capable de trier les fractions décimales. C'est, cependant, une bonne idée de contrôler awk
le format de sortie de, car avec le format par défaut %.6g
, rand()
affichera le nombre occasionnel en notation exponentielle . Bien que le brassage jusqu'à 1 million de lignes soit sans doute suffisant dans la pratique, il est facile de prendre en charge plus de lignes sans payer beaucoup de pénalité de performance; par exemple %.17f
.
sort
devrait être capable de gérer des fractions décimales (même avec des milliers de séparateurs, comme je viens de le remarquer).
Ruby FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
puts ARGF.readlines.shuffle
, vous pouvez le faire fonctionner avec les arguments stdin input et filename.
ruby -e 'puts $<.sort_by{rand}'
- ARGF est déjà un énumérable, nous pouvons donc mélanger les lignes en les triant par valeurs aléatoires.
Un liner pour Python basé sur la réponse de scai , mais a) prend stdin, b) rend le résultat reproductible avec la graine, c) ne sélectionne que 200 de toutes les lignes.
$ cat file | python -c "import random, sys;
random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
> 200lines.txt
Une manière simple et intuitive serait d'utiliser shuf
.
Exemple:
Supposons words.txt
que:
the
an
linux
ubuntu
life
good
breeze
Pour mélanger les lignes, procédez comme suit:
$ shuf words.txt
ce qui jetterait les lignes mélangées à la sortie standard ; Donc, vous devez le diriger vers un fichier de sortie comme:
$ shuf words.txt > shuffled_words.txt
Un tel parcours aléatoire pourrait donner:
breeze
the
linux
an
ubuntu
good
life
Ceci est un script python que j'ai enregistré sous rand.py dans mon dossier personnel:
#!/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()
Sur Mac OSX sort -R
et shuf
ne sont pas disponibles, vous pouvez donc les alias dans votre bash_profile comme:
alias shuf='python rand.py'
Si, comme moi, vous êtes venu ici pour chercher une alternative à shuf
macOS, utilisez-le randomize-lines
.
Installez le package randomize-lines
(homebrew), qui a une rl
commande qui a des fonctionnalités similaires à 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 coreutils
fournit le shuf
binaire en tant que gshuf
.
Si vous avez installé Scala, voici une ligne pour mélanger l'entrée:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
Cette fonction bash a la dépendance minimale (uniquement sort et bash):
shuf() {
while read -r x;do
echo $RANDOM$'\x1f'$x
done | sort |
while IFS=$'\x1f' read -r x y;do
echo $y
done
}
awk
solution assistée par l'OP , mais les performances seront un problème avec une entrée plus importante; votre utilisation d'une seule $RANDOM
valeur ne mélange correctement que jusqu'à 32 768 lignes d'entrée; bien que vous puissiez étendre cette plage, cela ne vaut probablement pas la peine: par exemple, sur ma machine, l'exécution de votre script sur 32 768 lignes d'entrée courtes prend environ 1 seconde, ce qui représente environ 150 fois la durée d'exécution shuf
et environ 10-15 fois aussi longtemps que awk
dure la solution assistée par le PO . Si vous pouvez compter sur votre sort
présence, vous awk
devriez également être là.
Dans Windows, vous pouvez essayer ce fichier de commandes pour vous aider à mélanger votre data.txt, l'utilisation du code de commandes est
C:\> type list.txt | shuffle.bat > maclist_temp.txt
Après avoir émis cette commande, maclist_temp.txt contiendra une liste aléatoire de lignes.
J'espère que cela t'aides.
Pas encore mentionné:
L' unsort
utilité. Syntaxe (quelque peu orientée playlist):
unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
[--identity] [--filenames[=profile]] [--separator sep] [--concatenate]
[--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null]
[--linefeed] [file ...]
msort
peut être mélangé par ligne, mais c'est généralement exagéré:
seq 10 | msort -jq -b -l -n 1 -c r