Je veux mélanger les lignes d'un fichier texte au hasard et créer un nouveau fichier. Le fichier peut avoir plusieurs milliers de lignes.
Comment puis-je faire cela avec cat
, awk
, cut
, etc.?
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; bien, vous obtenez l'image. Il a été souligné que sort -R
ne mélange pas vraiment mais trie les éléments en fonction de leur valeur de hachage.
[Note de la rédaction: sort -R
presque mélange, sauf que dupliquer lignes/clés de tri se terminent toujours côte à côte. En d'autres termes: avec unique lignes/touches d'entrée unique, il s'agit d'un véritable mélange. S'il est vrai que l'ordre de sortie est déterminé par valeurs de hachage, le caractère aléatoire provient du choix d'un hachage aléatoire fonction - voir manuel .]
Perl one-liner serait une version simple de la solution de Maxim
Perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
Cette réponse complète les nombreuses grandes réponses existantes des manières suivantes:
Les réponses existantes sont empaquetées dans flexible fonctions Shell:
stdin
entrées, mais alternativement aussi nom du fichier argumentsSIGPIPE
de la manière habituelle (fin silencieuse avec le code de sortie 141
), par opposition à la rupture bruyante. Cela est important lorsque vous transmettez la sortie de fonction à un tuyau qui a été fermé tôt, par exemple lorsque vous transmettez à head
.Une comparaison performance est effectuée.
awk
, sort
ET cut
, adaptée de la réponse du 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))
' "$@"; }
shuf() { Ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "$@"; }
Comparaison de performance:
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 temps varient selon le système d'exploitation utilisé, les spécifications de la machine, awk
implémentation utilisée (par exemple, la version BSD awk
utilisée sur OSX est généralement plus lente que GNU awk
et surtout mawk
), cela devrait fournir un aperçu général. sens de relatif performance.
Le fichier Input est un fichier 1 million de lignes produit avec seq -f 'line %.0f' 1000000
.
Les temps sont listé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 une comparaison plus poussée, les solutions non présentées dans les fonctions ci-dessus:
sort -R
(pas une véritable lecture aléatoire s'il y a des lignes d'entrée dupliquées) 10.661s
- Allouer plus de mémoire ne semble pas faire la différence24.229s
bash
BOUCLES + sort
32.593s
Conclusions:
shuf
, si vous le pouvez - de loin le plus rapide.awk
+ sort
+ cut
conforme à POSIX en dernier recours; quelle implémentation awk
vous utilisez (mawk
est plus rapide que GNU awk
, BSD awk
est la plus lente).sort -R
, bash
et Scala.J'utilise un petit script Perl, que j'appelle "unsort":
#!/usr/bin/Perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
J'ai aussi une version délimitée par NULL, appelée "unsort0" ... très pratique à utiliser avec find -print0 et ainsi de suite.
PS: J'ai voté 'shuf' aussi, je ne savais pas qu'il y en avait dans les coreutils ces jours-ci ... ce qui précède peut encore être utile si vos systèmes n'ont pas 'shuf'.
Voici un premier essai simple pour le codeur, mais aussi pour 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
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
sortie
$ cat file
1
2
3
4
5
6
7
8
9
10
$ ./Shell.sh
7
5
10
9
6
8
2
1
3
4
Un one-liner pour le 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 voir cet article pour connaître les inconvénients de random.shuffle()
de python. Cela ne fonctionnera pas bien avec beaucoup (plus de 2080) éléments.
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 la multiplication des zéros (%06d
) et de Rand()
le fait fonctionner correctement également sur les systèmes où sort
ne comprend pas les nombres. Il peut être trié par ordre lexicographique (par exemple, une comparaison normale de chaînes).
Ruby FTW:
ls | Ruby -e 'puts STDIN.readlines.shuffle'
Une ligne pour Python basée sur la réponse de scai , mais a) prend stdin, b) rend le résultat reproductible avec la valeur de départ, c) sélectionne seulement 200 lignes.
$ cat file | python -c "import random, sys;
random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
> 200lines.txt
Une méthode simple et intuitive consisterait à utiliser shuf
.
Exemple:
Supposez words.txt
comme:
the
an
linux
ubuntu
life
good
breeze
Pour mélanger les lignes, faites:
$ shuf words.txt
ce qui jetterait les lignes mélangées à sortie standard _; Donc, vous devez pipe le transformer en un fichier de sortie comme:
$ shuf words.txt > shuffled_words.txt
Un de ces shuffle run _ pourrait donner:
breeze
the
linux
an
ubuntu
good
life
Nous avons un paquet pour faire le travail même:
Sudo apt-get install randomize-lines
Exemple:
Créez une liste ordonnée de nombres et enregistrez-la en 1000.txt:
seq 1000 > 1000.txt
pour le mélanger, utilisez simplement
rl 1000.txt
Si comme moi, vous êtes venu chercher une alternative à shuf
pour macOS, utilisez randomize-lines
.
Installez le package randomize-lines
(homebrew), qui contient une commande rl
qui a une fonctionnalité similaire à 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
C’est un script python que j’ai enregistré sous le nom Rand.py dans mon dossier personnel:
#!/bin/python
import sys
import random
if __== '__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 nommer dans votre profil bash sous:
alias shuf='python Rand.py'
Si vous avez installé Scala, voici une ligne pour mélanger les entrées:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
Cette fonction bash a la dépendance minimale (seulement 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
}
Pas encore mentionné:
Le unsort
util. Syntaxe (plutôt orientée sur les listes de lecture):
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 excessif:
seq 10 | msort -jq -b -l -n 1 -c r
Une autre variante awk
:
#!/usr/bin/awk -f
# usage:
# awk -f randomize_lines.awk lines.txt
# usage after "chmod +x randomize_lines.awk":
# randomize_lines.awk lines.txt
BEGIN {
FS = "\n";
srand();
}
{
lines[ Rand()] = $0;
}
END {
for( k in lines ){
print lines[k];
}
}
Dans Windows, vous pouvez essayer ce fichier de commandes pour vous aider à mélanger votre data.txt.
C:\> type list.txt | shuffle.bat > maclist_temp.txt
Après avoir exécuté cette commande, maclist_temp.txt contiendra une liste aléatoire de lignes.
J'espère que cela t'aides.