Voglio mescolare le righe di un file di testo in modo casuale e creare un nuovo file. Il file può contenere diverse migliaia di righe.
Come posso farlo con cat
, awk
, cut
, ecc?
Voglio mescolare le righe di un file di testo in modo casuale e creare un nuovo file. Il file può contenere diverse migliaia di righe.
Come posso farlo con cat
, awk
, cut
, ecc?
Risposte:
È possibile utilizzare shuf
. Almeno su alcuni sistemi (non sembra essere in POSIX).
Come ha sottolineato jleedev: sort -R
potrebbe anche essere un'opzione. Almeno su alcuni sistemi; bene, ottieni l'immagine. È stato sottolineato che sort -R
non si mescola realmente, ma invece ordina gli oggetti in base al loro valore di hash.
[Nota del redattore: sort -R
quasi si mescolano, tranne per il fatto che le righe duplicate / i tasti di ordinamento finiscono sempre uno accanto all'altro . In altre parole: solo con righe / tasti di input univoci è un vero shuffle. Mentre è vero che l'ordine di output è determinato dai valori di hash , la casualità deriva dalla scelta di una funzione di hash casuale - consultare il manuale .]
shuf
e sort -R
differiscono leggermente, perché sort -R
ordina casualmente gli elementi in base all'hash di loro, che è, sort -R
metterà insieme gli elementi ripetuti, mentre shuf
mescola tutti gli elementi in modo casuale.
brew install coreutils
gshuf ...
sort -R
e shuf
dovrebbe essere visto come completamente diverso. sort -R
è deterministico. Se lo chiami due volte in momenti diversi sullo stesso input otterrai la stessa risposta. shuf
d'altra parte, produce output randomizzati, quindi molto probabilmente darà output diversi sullo stesso input.
Perl one-liner sarebbe una versione semplice della soluzione di Maxim
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
\n
; sì, \n
deve essere presente - e in genere lo è - altrimenti otterrai ciò che descrivi.
<STDIN>
con <>
, quindi la soluzione funziona anche con l'input dei file .
Questa risposta completa le molte grandi risposte esistenti nei seguenti modi:
Le risposte esistenti sono raggruppate in funzioni shell flessibili :
stdin
input, ma in alternativa anche argomenti di nome fileSIGPIPE
nel solito modo (terminazione silenziosa con codice di uscita 141
), anziché interrompere in modo rumoroso. Ciò è importante quando si esegue il piping dell'uscita della funzione su un tubo che viene chiuso in anticipo, ad esempio quando si esegue il piping a head
.Viene effettuato un confronto delle prestazioni .
awk
, sort
ecut
adattata dalla risposta dell'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))
' "$@"; }
Vedere la sezione inferiore per una versione Windows di questa funzione.
shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "$@"; }
Confronto delle prestazioni:
Nota: questi numeri sono stati ottenuti su un iMac di fine 2012 con Intel Core i5 a 3,2 GHz e Fusion Drive, con OSX 10.10.3. Mentre i tempi variano a seconda del sistema operativo utilizzato, delle specifiche della macchina, awk
dell'implementazione utilizzata (ad esempio, la awk
versione BSD utilizzata su OSX è generalmente più lenta di GNU awk
e soprattutto mawk
), ciò dovrebbe fornire un senso generale delle prestazioni relative .
Il file di input è un file di 1 milione di righe prodotto con seq -f 'line %.0f' 1000000
.
I tempi sono elencati in ordine crescente (prima il più veloce):
shuf
0.090s
0.289s
0.589s
1.342s
con Python 2.7.6; 2.407s
(!) con Python 3.4.2awk
+ sort
+cut
3.003s
con BSD awk
; 2.388s
con GNU awk
(4.1.1); 1.811s
con mawk
(1.3.4);Per un ulteriore confronto, le soluzioni non impacchettate come funzioni sopra:
sort -R
(non un vero shuffle se ci sono righe di input duplicate)
10.661s
- l'allocazione di più memoria non sembra fare la differenza24.229s
bash
loop + sort
32.593s
Conclusioni :
shuf
, se puoi , è di gran lunga il più veloce.awk
+ conforme a POSIX come ultima risorsasort
cut
; quale awk
implementazione usi è importante ( mawk
è più veloce di GNU awk
, BSD awk
è più lenta).sort -R
, bash
loop e Scala.Versioni di Windows della soluzione Python (il codice Python è identico, ad eccezione delle variazioni di quotazione e rimozione delle istruzioni relative al segnale, che non sono supportate su Windows):
$OutputEncoding
se desideri inviare caratteri non ASCII tramite la 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
}
Si noti che PowerShell può eseguire il shuffle nativo tramite il suo Get-Random
cmdlet (sebbene le prestazioni possano essere un problema); per esempio:
Get-Content someFile.txt | Get-Random -Count ([int]::MaxValue)
cmd.exe
(un file batch):Salva su file shuf.cmd
, ad esempio:
@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);
dalla soluzione originale è sufficiente e mantiene la flessibilità di poter anche passare gli argomenti del nome file - non c'è bisogno di cambiare nient'altro (tranne che per la citazione) - si prega di consultare la nuova sezione che ho aggiunto al parte inferiore.
Uso un piccolo script perl, che io chiamo "unsort":
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
Ho anche una versione delimitata da NULL, chiamata "unsort0" ... utile per l'uso con find -print0 e così via.
PS: Anche io ho votato "shuf", non avevo idea che ci fosse in coreutils in questi giorni ... quanto sopra potrebbe ancora essere utile se i tuoi sistemi non hanno "shuf".
<STDIN>
con <>
per far funzionare la soluzione anche con l'input dai file .
Ecco un primo tentativo facile sul programmatore ma difficile sulla CPU che antepone un numero casuale a ciascuna riga, li ordina e quindi elimina il numero casuale da ciascuna riga. In effetti, le linee sono ordinate casualmente:
cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
head myfile | awk ...
. Quindi lo cambio semplicemente in gatto; ecco perché è stato lasciato lì.
-k1 -n
ordinare, poiché l'output di awk rand()
è un decimale tra 0 e 1 e perché tutto ciò che conta è che in qualche modo viene riordinato. -k1
potrebbe aiutare ad accelerarlo ignorando il resto della linea, sebbene l'output di rand () dovrebbe essere abbastanza unico da cortocircuitare il confronto.
cat filename |
(o < filename |
) che ricordare come ogni singolo programma accetta (o meno) l'input di file.
ecco una sceneggiatura fantastica
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
produzione
$ 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
con sort
e cut
. Per non più di diverse migliaia di righe non fa molta differenza, ma con un numero di righe maggiore è importante (la soglia dipende awk
dall'implementazione utilizzata). Una leggera semplificazione sarebbe quella di sostituire le linee while (1){
e if (e==d) {break}
con while (e<d)
.
Un one-liner per Python:
python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile
E per stampare una sola riga casuale:
python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile
Ma vedi questo post per gli svantaggi di Python random.shuffle()
. Non funzionerà bene con molti (più di 2080) elementi.
/dev/urandom
fa. Per utilizzarlo da Python: random.SystemRandom().shuffle(L)
.
.readLines()
restituisce le righe con una nuova riga finale.
La semplice funzione basata su awk farà il lavoro:
shuffle() {
awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}
utilizzo:
any_command | shuffle
Questo dovrebbe funzionare su quasi tutti gli UNIX. Testato su Linux, Solaris e HP-UX.
Aggiornare:
Si noti che i primi zeri ( %06d
) e la rand()
moltiplicazione lo fanno funzionare correttamente anche su sistemi in cui i sort
numeri non sono compresi. Può essere ordinato in base all'ordine lessicografico (noto anche come confronto di stringhe normali).
"$@"
, funzionerà anche con i file come input. Non c'è motivo di moltiplicarsi rand()
, perché sort -n
è in grado di ordinare le frazioni decimali. È comunque una buona idea controllare awk
il formato di output, perché con il formato predefinito %.6g
, rand()
produrrà il numero occasionale in notazione esponenziale . Mentre mescolare fino a 1 milione di linee è probabilmente abbastanza pratico, è facile supportare più linee senza pagare gran parte della penalità prestazionale; es %.17f
.
sort
dovrebbe essere in grado di gestire le frazioni decimali (anche con migliaia di separatori, come ho appena notato).
Ruby FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
puts ARGF.readlines.shuffle
, puoi farlo funzionare con argomenti sia di input che di nome file stdin.
ruby -e 'puts $<.sort_by{rand}'
: ARGF è già un elenco numeroso, quindi possiamo mescolare le linee ordinandole per valori casuali.
Un liner per Python basato sulla risposta di Scai , ma a) prende lo stdin, b) rende il risultato ripetibile con seed, c) seleziona solo 200 di tutte le righe.
$ cat file | python -c "import random, sys;
random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
> 200lines.txt
Un modo semplice ed intuitivo sarebbe usare shuf
.
Esempio:
Assumi words.txt
come:
the
an
linux
ubuntu
life
good
breeze
Per mescolare le linee, fai:
$ shuf words.txt
che avrebbe gettato le linee mescolate in output standard ; Quindi, devi reindirizzarlo a un file di output come:
$ shuf words.txt > shuffled_words.txt
Una corsa di questo tipo potrebbe produrre:
breeze
the
linux
an
ubuntu
good
life
Questo è uno script Python che ho salvato come rand.py nella mia cartella home:
#!/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()
Su Mac OSX sort -R
e shuf
non sono disponibili, quindi puoi alias nel tuo bash_profile come:
alias shuf='python rand.py'
Se come me sei venuto qui per cercare un'alternativa a shuf
macOS, allora usa randomize-lines
.
Installa il randomize-lines
pacchetto (homebrew), che ha un rl
comando con funzionalità simili 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 coreutils
fornisce il file shuf
binario come gshuf
.
Se hai installato Scala, ecco un one-liner per mescolare l'input:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
Questa funzione bash ha la dipendenza minima (solo ordinamento e 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
soluzione assistita dall'OP , ma le prestazioni saranno un problema con input più ampi; l'uso di un singolo $RANDOM
valore mescola correttamente solo fino a 32.768 righe di input; mentre potresti estendere questo intervallo, probabilmente non ne vale la pena: ad esempio, sulla mia macchina, l'esecuzione dello script su 32.768 righe di input brevi richiede circa 1 secondo, ovvero circa 150 volte il tempo di esecuzione shuf
e circa 10-15 volte fino a quando awk
impiegherà la soluzione assistita dall'OP . Se puoi fare affidamento sulla sort
presenza, awk
dovrebbe esserci anche tu .
In windows Puoi provare questo file batch per aiutarti a mescolare i tuoi data.txt, L'utilizzo del codice batch è
C:\> type list.txt | shuffle.bat > maclist_temp.txt
Dopo aver emesso questo comando, maclist_temp.txt conterrà un elenco casuale di righe.
Spero che questo ti aiuti.
Non ancora menzionato:
L' unsort
utilità Sintassi (in qualche modo orientata alla playlist):
unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
[--identity] [--filenames[=profile]] [--separator sep] [--concatenate]
[--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null]
[--linefeed] [file ...]
msort
può mescolare per riga, ma di solito è eccessivo:
seq 10 | msort -jq -b -l -n 1 -c r