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 -Rpotrebbe anche essere un'opzione. Almeno su alcuni sistemi; bene, ottieni l'immagine. È stato sottolineato che sort -Rnon 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 .]
shufe sort -Rdifferiscono leggermente, perché sort -Rordina casualmente gli elementi in base all'hash di loro, che è, sort -Rmetterà insieme gli elementi ripetuti, mentre shufmescola tutti gli elementi in modo casuale.
brew install coreutilsgshuf ...
sort -Re shufdovrebbe essere visto come completamente diverso. sort -Rè deterministico. Se lo chiami due volte in momenti diversi sullo stesso input otterrai la stessa risposta. shufd'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ì, \ndeve 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 :
stdininput, ma in alternativa anche argomenti di nome fileSIGPIPEnel 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, sortecut 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, awkdell'implementazione utilizzata (ad esempio, la awkversione BSD utilizzata su OSX è generalmente più lenta di GNU awke 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.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);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.229sbash loop + sort
32.593sConclusioni :
shuf, se puoi , è di gran lunga il più veloce.awk+ conforme a POSIX come ultima risorsasortcut ; quale awkimplementazione usi è importante ( mawkè più veloce di GNU awk, BSD awkè più lenta).sort -R, bashloop 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):
$OutputEncodingse 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-Randomcmdlet (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 -nordinare, poiché l'output di awk rand()è un decimale tra 0 e 1 e perché tutto ciò che conta è che in qualche modo viene riordinato. -k1potrebbe 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
awkcon sorte cut. Per non più di diverse migliaia di righe non fa molta differenza, ma con un numero di righe maggiore è importante (la soglia dipende awkdall'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/urandomfa. 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 sortnumeri 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 awkil 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.
sortdovrebbe 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.txtcome:
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 -Re shufnon 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 shufmacOS, allora usa randomize-lines.
Installa il randomize-linespacchetto (homebrew), che ha un rlcomando 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 coreutilsfornisce il file shufbinario 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
}
awksoluzione assistita dall'OP , ma le prestazioni saranno un problema con input più ampi; l'uso di un singolo $RANDOMvalore 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 shufe circa 10-15 volte fino a quando awkimpiegherà la soluzione assistita dall'OP . Se puoi fare affidamento sulla sortpresenza, awkdovrebbe 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' unsortutilità 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