web-dev-qa-db-fra.com

Comment puis-je mélanger les lignes d'un fichier texte sur la ligne de commande Unix ou dans un script Shell?

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.?

251
Ruggiero Spearman

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 -Rpresque 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 .]

315
Joey

Perl one-liner serait une version simple de la solution de Maxim

Perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
77
Moonyoung Kang

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:

    • Les fonctions prennent non seulement stdin entrées, mais alternativement aussi nom du fichier arguments
    • Les fonctions prennent des mesures supplémentaires pour gérer SIGPIPE 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.


  • Fonction POSIX conforme basée sur 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
  • Ruby 2.0.0
    • 0.289s
  • Perl 5.18.2
    • 0.589s
  • Python
    • 1.342s avec Python 2.7.6; 2.407s (!) avec Python 3.4.2
  • awk + 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érence
  • Scala
    • 24.229s
  • bash BOUCLES + sort
    • 32.593s

Conclusions:

  • Utilisez shuf, si vous le pouvez - de loin le plus rapide.
  • Ruby fait bien, suivi de Perl.
  • Python est sensiblement plus lent que Ruby et Perl, et si on compare les versions de Python, 2.7.6 est un peu plus rapide que 3.4.1
  • Utilisez le combo 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).
  • Éloignez-vous des boucles sort -R, bash et Scala.
53
mklement0

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'.

27
NickZoic

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
17
Ruggiero Spearman

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
16
ghostdog74

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.

11
scai

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).

8
Michał Šrajer

Ruby FTW:

ls | Ruby -e 'puts STDIN.readlines.shuffle'
7
hoffmanc

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
6
dfrankow

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
4
kmario23

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
4
navigaid

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
3
Ahmad Awais

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'
3
Jeff Wu

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)'
2
swartzrock

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
}
1
Meow

Pas encore mentionné:

  1. 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 ...]
    
  2. msort peut être mélangé par ligne, mais c'est généralement excessif:

    seq 10 | msort -jq -b -l -n 1 -c r
    
0
agc

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];
  }
}
0
biziclop

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.

0
Ayfan