Saya ingin mengocok garis file teks secara acak dan membuat file baru. File mungkin memiliki beberapa ribu baris.
Bagaimana saya bisa melakukannya dengan cat
, awk
, cut
, dll?
Saya ingin mengocok garis file teks secara acak dan membuat file baru. File mungkin memiliki beberapa ribu baris.
Bagaimana saya bisa melakukannya dengan cat
, awk
, cut
, dll?
Jawaban:
Anda bisa menggunakannya shuf
. Paling tidak pada beberapa sistem (tampaknya tidak ada dalam POSIX).
Seperti yang ditunjukkan oleh jleedev: sort -R
mungkin juga menjadi pilihan. Setidaknya pada beberapa sistem; yah, Anda mendapatkan fotonya. Telah ditunjukkan bahwa sort -R
tidak benar-benar mengacak tetapi mengurutkan item sesuai dengan nilai hash mereka.
[Catatan Editor: sort -R
hampir mengocok, kecuali bahwa garis duplikat / kunci sortir selalu berakhir bersebelahan . Dengan kata lain: hanya dengan jalur input / kunci unik yang benar-benar acak. Meskipun benar bahwa urutan output ditentukan oleh nilai hash , keacakan berasal dari memilih fungsi hash acak - lihat manual .]
shuf
dan sort -R
sedikit berbeda, karena sort -R
memerintahkan elemen secara acak sesuai hash , yang sort -R
akan menyatukan elemen yang diulang, sementara shuf
mengocok semua elemen secara acak.
brew install coreutils
gshuf ...
sort -R
dan shuf
harus dilihat sebagai sangat berbeda. sort -R
bersifat deterministik. Jika Anda memanggilnya dua kali pada waktu yang berbeda pada input yang sama, Anda akan mendapatkan jawaban yang sama. shuf
, di sisi lain, menghasilkan output acak, sehingga kemungkinan besar akan memberikan output berbeda pada input yang sama.
Perl one-liner akan menjadi versi sederhana dari solusi Maxim
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
\n
; ya, yang \n
harus hadir - dan itu biasanya adalah - jika tidak Anda akan mendapatkan apa yang Anda gambarkan.
<STDIN>
dengan <>
, jadi solusinya bekerja dengan input dari file juga.
Jawaban ini melengkapi banyak jawaban hebat yang ada dengan cara-cara berikut:
Jawaban yang ada dikemas ke dalam fungsi shell fleksibel :
stdin
input, tetapi juga argumen nama fileSIGPIPE
dengan cara biasa (penghentian diam dengan kode keluar 141
), sebagai lawan melanggar berisik. Hal ini penting ketika pipa output fungsi untuk pipa yang ditutup lebih awal, seperti ketika pipa ke head
.Sebuah perbandingan kinerja dibuat.
awk
, sort
dancut
, diadaptasi dari jawaban OP sendiri :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))
' "$@"; }
Lihat bagian bawah untuk versi Windows dari fungsi ini.
shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "$@"; }
Perbandingan kinerja:
Catatan: Angka-angka ini diperoleh pada iMac akhir 2012 dengan 3,2 GHz Intel Core i5 dan Fusion Drive, menjalankan OSX 10.10.3. Sementara pengaturan waktu akan bervariasi dengan OS yang digunakan, spesifikasi mesin, awk
implementasi yang digunakan (misalnya, awk
versi BSD yang digunakan pada OSX biasanya lebih lambat daripada GNU awk
dan khususnya mawk
), ini harus memberikan perasaan umum tentang kinerja relatif .
File input adalah file 1-juta-baris yang dihasilkan seq -f 'line %.0f' 1000000
.
Waktu terdaftar dalam urutan menaik (tercepat pertama):
shuf
0.090s
0.289s
0.589s
1.342s
dengan Python 2.7.6; 2.407s
(!) dengan Python 3.4.2awk
+ sort
+cut
3.003s
dengan BSD awk
; 2.388s
dengan GNU awk
(4.1.1); 1.811s
dengan mawk
(1.3.4);Untuk perbandingan lebih lanjut, solusi yang tidak dikemas sebagai fungsi di atas:
sort -R
(bukan shuffle sejati jika ada jalur input duplikat)
10.661s
- mengalokasikan lebih banyak memori sepertinya tidak membuat perbedaan24.229s
bash
loop + sort
32.593s
Kesimpulan :
shuf
, jika Anda bisa - ini yang tercepat sejauh ini.awk
+ yang sesuai dengan POSIX sebagai pilihan terakhirsort
cut
; yang awk
pelaksanaannya Anda menggunakan hal-hal ( mawk
lebih cepat dari GNU awk
, BSD awk
paling lambat).sort -R
, bash
loop, dan Scala.Versi Windows dari solusi Python (kode Python identik, kecuali untuk variasi dalam kutipan dan penghapusan pernyataan terkait sinyal, yang tidak didukung pada Windows):
$OutputEncoding
jika Anda ingin mengirim karakter non-ASCII melalui pipa):# 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
}
Perhatikan bahwa PowerShell dapat secara acak mengocok melalui Get-Random
cmdlet -nya (meskipun kinerja mungkin menjadi masalah); misalnya:
Get-Content someFile.txt | Get-Random -Count ([int]::MaxValue)
cmd.exe
(file batch):Simpan ke file shuf.cmd
, misalnya:
@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);
dari solusi asli sudah cukup, dan mempertahankan fleksibilitas karena juga mampu melewati argumen nama file - tidak perlu mengubah apa pun (kecuali untuk mengutip) - silakan lihat bagian baru yang saya tambahkan di bawah.
Saya menggunakan skrip perl kecil, yang saya sebut "unsort":
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
Saya juga punya versi terbatas-NULL, yang disebut "unsort0" ... berguna untuk digunakan dengan find -print0 dan seterusnya.
PS: Dipilih sebagai 'shuf' juga, saya tidak tahu bahwa ada di coreutils hari ini ... di atas mungkin masih berguna jika sistem Anda tidak memiliki 'shuf'.
<STDIN>
dengan <>
untuk membuat pekerjaan solusi dengan masukan dari file juga.
Berikut ini adalah percobaan pertama yang mudah pada koder tetapi sulit pada CPU yang menambahkan angka acak ke setiap baris, mengurutkan mereka dan kemudian menghapus nomor acak dari setiap baris. Akibatnya, garis diurutkan secara acak:
cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
head myfile | awk ...
. Lalu saya hanya mengubahnya menjadi kucing; itu sebabnya dibiarkan di sana.
-k1 -n
disortir, karena output awk rand()
adalah desimal antara 0 dan 1 dan karena yang penting adalah bahwa ia akan disusun ulang entah bagaimana. -k1
mungkin membantu mempercepatnya dengan mengabaikan sisa baris, meskipun output rand () harus cukup unik untuk membuat hubungan pendek perbandingan.
cat filename |
(atau < filename |
) daripada mengingat bagaimana setiap program mengambil input file (atau tidak).
inilah skrip 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
keluaran
$ 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
dengan sort
dan cut
. Untuk tidak lebih dari beberapa ribu baris itu tidak membuat banyak perbedaan, tetapi dengan jumlah garis yang lebih tinggi itu penting (ambang batas tergantung pada awk
implementasi yang digunakan). Penyederhanaan sedikit akan menggantikan while (1){
dan if (e==d) {break}
dengan garis while (e<d)
.
Satu kalimat untuk python:
python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile
Dan untuk mencetak hanya satu baris acak:
python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile
Tetapi lihat posting ini untuk kelemahan python random.shuffle()
. Itu tidak akan bekerja dengan baik dengan banyak (lebih dari 2080) elemen.
/dev/urandom
halnya. Untuk menggunakannya dari Python: random.SystemRandom().shuffle(L)
.
.readLines()
mengembalikan garis dengan baris baru tambahan.
Fungsi berbasis awk sederhana akan melakukan pekerjaan:
shuffle() {
awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}
pemakaian:
any_command | shuffle
Ini harus bekerja pada hampir semua UNIX. Diuji di Linux, Solaris dan HP-UX.
Memperbarui:
Perhatikan, bahwa nol di depan ( %06d
) dan rand()
multiplikasi membuatnya bekerja dengan baik juga pada sistem yang sort
tidak memahami angka. Itu dapat diurutkan melalui urutan leksikografis (alias perbandingan string normal).
"$@"
, itu juga akan berfungsi dengan file sebagai input. Tidak ada alasan untuk berkembang biak rand()
, karena sort -n
mampu mengurutkan pecahan desimal. Namun, ide yang baik untuk mengontrol awk
format output, karena dengan format default %.6g
,, rand()
akan menampilkan angka sesekali dalam notasi eksponensial . Sementara mengocok hingga 1 juta baris bisa dibilang cukup dalam praktiknya, mudah untuk mendukung lebih banyak lini tanpa membayar banyak penalti kinerja; mis %.17f
.
sort
harus mampu menangani pecahan desimal (bahkan dengan ribuan pemisah, seperti yang baru saja saya perhatikan).
Ruby FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
puts ARGF.readlines.shuffle
, Anda bisa membuatnya bekerja dengan input stdin dan argumen nama file.
ruby -e 'puts $<.sort_by{rand}'
- ARGF sudah merupakan enumerable, jadi kita dapat mengacak garis dengan mengurutkannya dengan nilai acak.
Satu liner untuk Python berdasarkan jawaban scai , tetapi a) mengambil stdin, b) membuat hasil berulang dengan seed, c) memilih hanya 200 dari semua baris.
$ cat file | python -c "import random, sys;
random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
> 200lines.txt
Cara yang sederhana dan intuitif akan digunakan shuf
.
Contoh:
Asumsikan words.txt
sebagai:
the
an
linux
ubuntu
life
good
breeze
Untuk mengocok garis, lakukan:
$ shuf words.txt
yang akan melempar garis yang dikocok ke output standar ; Jadi, Anda harus mem - pipe- nya ke file output seperti:
$ shuf words.txt > shuffled_words.txt
Satu menjalankan acak seperti itu dapat menghasilkan:
breeze
the
linux
an
ubuntu
good
life
Ini adalah skrip python yang saya simpan sebagai rand.py di folder rumah saya:
#!/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()
Di Mac OSX sort -R
dan shuf
tidak tersedia sehingga Anda dapat alias ini di bash_profile Anda sebagai:
alias shuf='python rand.py'
Jika seperti saya Anda datang ke sini untuk mencari alternatif shuf
untuk MacOS kemudian gunakan randomize-lines
.
Instal randomize-lines
(homebrew) paket, yang memiliki rl
perintah yang memiliki fungsi serupa dengan 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
menyediakan shuf
biner sebagai gshuf
.
Jika Anda telah menginstal Scala, berikut ini adalah one-liner untuk mengacak input:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
Fungsi bash ini memiliki ketergantungan minimal (hanya sort and 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
, tetapi kinerja akan menjadi masalah dengan input yang lebih besar; penggunaan $RANDOM
nilai tunggal Anda mengocok dengan benar hanya hingga 32.768 jalur input; sementara Anda dapat memperluas rentang itu, itu mungkin tidak sepadan: misalnya, di komputer saya, menjalankan skrip Anda di 32.768 jalur input pendek membutuhkan waktu sekitar 1 detik, yaitu sekitar 150 kali lebih lama dari shuf
waktu berjalan , dan sekitar 10-15 kali selama awk
solusi yang dibantu sendiri OP mengambil. Jika Anda bisa mengandalkan sort
kehadiran, awk
harus ada di sana juga.
Di windows Anda dapat mencoba file batch ini untuk membantu Anda mengocok data.txt Anda, Penggunaan kode batch
C:\> type list.txt | shuffle.bat > maclist_temp.txt
Setelah mengeluarkan perintah ini, maclist_temp.txt akan berisi daftar garis acak.
Semoga ini membantu.
Belum disebutkan:
The unsort
util. Sintaks (berorientasi playlist):
unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
[--identity] [--filenames[=profile]] [--separator sep] [--concatenate]
[--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null]
[--linefeed] [file ...]
msort
dapat mengocok per baris, tetapi biasanya berlebihan:
seq 10 | msort -jq -b -l -n 1 -c r