Tôi muốn xáo trộn các dòng của tệp văn bản một cách ngẫu nhiên và tạo một tệp mới. Các tập tin có thể có vài ngàn dòng.
Làm thế nào tôi có thể làm điều đó với cat
, awk
, cut
, vv?
Tôi muốn xáo trộn các dòng của tệp văn bản một cách ngẫu nhiên và tạo một tệp mới. Các tập tin có thể có vài ngàn dòng.
Làm thế nào tôi có thể làm điều đó với cat
, awk
, cut
, vv?
Câu trả lời:
Bạn có thể sử dụng shuf
. Trên một số hệ thống ít nhất (dường như không có trong POSIX).
Như jleedev đã chỉ ra: sort -R
cũng có thể là một lựa chọn. Trên một số hệ thống ít nhất; Vâng, bạn có được hình ảnh. Nó đã được chỉ ra rằng sort -R
không thực sự xáo trộn mà thay vào đó sắp xếp các mục theo giá trị băm của chúng.
[Ghi chú của biên tập viên: sort -R
gần như xáo trộn, ngoại trừ các dòng / khóa sắp xếp trùng lặp luôn luôn nằm cạnh nhau . Nói cách khác: chỉ với các dòng / khóa đầu vào duy nhất thì nó là một sự xáo trộn thực sự. Trong khi đó là sự thật rằng thứ tự đầu ra được xác định bởi giá trị băm , tính ngẫu nhiên xuất phát từ việc lựa chọn một băm ngẫu nhiên chức năng - xem tay ].
shuf
và sort -R
hơi khác nhau, bởi vì sort -R
ngẫu nhiên sắp xếp các phần tử theo hàm băm của chúng, nghĩa là sort -R
sẽ đặt các phần tử lặp lại với nhau, trong khi shuf
xáo trộn tất cả các phần tử một cách ngẫu nhiên.
brew install coreutils
, sau đó sử dụng gshuf ...
(:
sort -R
và shuf
nên được xem là hoàn toàn khác nhau. sort -R
mang tính quyết định. Nếu bạn gọi nó hai lần vào các thời điểm khác nhau trên cùng một đầu vào, bạn sẽ nhận được cùng một câu trả lời. shuf
mặt khác, tạo ra đầu ra ngẫu nhiên, do đó rất có thể sẽ cung cấp đầu ra khác nhau trên cùng một đầu vào.
Perl one-liner sẽ là phiên bản đơn giản của giải pháp Maxim's
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
\n
; vâng, điều đó \n
phải có mặt - và thông thường là - nếu không bạn sẽ nhận được những gì bạn mô tả.
<STDIN>
bằng <>
, vì vậy giải pháp cũng hoạt động với đầu vào từ các tệp .
Câu trả lời này bổ sung cho nhiều câu trả lời tuyệt vời hiện có theo các cách sau:
Các câu trả lời hiện có được đóng gói thành các hàm shell linh hoạt :
stdin
nhập, mà cả các đối số tên tệpSIGPIPE
theo cách thông thường (chấm dứt yên tĩnh với mã thoát 141
), trái ngược với việc phá vỡ ồn ào. Điều này rất quan trọng khi đường ống đầu ra chức năng đến một đường ống được đóng sớm, chẳng hạn như khi đường ống đến head
.Một so sánh hiệu suất được thực hiện.
awk
, sort
vàcut
, chuyển thể từ các câu trả lời của riêng của 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))
' "$@"; }
Xem phần dưới cùng cho phiên bản Windows của chức năng này.
shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "$@"; }
So sánh hiệu suất:
Lưu ý: Những con số này đã thu được trên iMac cuối năm 2012 với Intel Core i5 3,2 GHz và Fusion Drive, chạy OSX 10.10.3. Mặc dù thời gian sẽ thay đổi theo hệ điều hành được sử dụng, thông số kỹ thuật của máy, awk
việc triển khai được sử dụng (ví dụ: awk
phiên bản BSD được sử dụng trên OSX thường chậm hơn GNU awk
và đặc biệt mawk
), điều này sẽ mang lại cảm giác chung về hiệu suất tương đối .
Tệp đầu vào là tệp 1 triệu dòng được tạo bằng seq -f 'line %.0f' 1000000
.
Thời gian được liệt kê theo thứ tự tăng dần (đầu tiên nhanh nhất):
shuf
0.090s
0.289s
0.589s
1.342s
với Python 2.7.6; 2.407s
(!) với Python 3.4.2awk
+ sort
+cut
3.003s
với BSD awk
; 2.388s
với GNU awk
(4.1.1); 1.811s
với mawk
(1.3.4);Để so sánh thêm, các giải pháp không được đóng gói như các chức năng trên:
sort -R
(không phải là một sự xáo trộn thực sự nếu có các dòng đầu vào trùng lặp)
10.661s
- phân bổ thêm bộ nhớ dường như không tạo ra sự khác biệt24.229s
bash
vòng lặp + sort
32.593s
Kết luận :
shuf
, nếu bạn có thể - đó là nhanh nhất cho đến nay.awk
+ sort
+ cut
kết hợp như một phương sách cuối cùng ; mà awk
thực hiện bạn sử dụng các vấn đề ( mawk
nhanh hơn GNU awk
, BSD awk
là chậm nhất).sort -R
, bash
vòng lặp và Scala.Các phiên bản Windows của giải pháp Python (mã Python giống hệt nhau, ngoại trừ các biến thể trong trích dẫn và loại bỏ các câu lệnh liên quan đến tín hiệu không được hỗ trợ trên Windows):
$OutputEncoding
nếu bạn muốn gửi các ký tự không phải ASCII qua đường ống):# 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
}
Lưu ý rằng PowerShell có thể tự xáo trộn thông qua Get-Random
lệnh ghép ngắn của nó (mặc dù hiệu suất có thể là một vấn đề); ví dụ:
Get-Content someFile.txt | Get-Random -Count ([int]::MaxValue)
cmd.exe
(một tệp bó):Lưu vào tập tin shuf.cmd
, ví dụ:
@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);
giải pháp ban đầu là đủ và vẫn giữ được tính linh hoạt để có thể vượt qua các đối số tên tệp - không cần thay đổi bất cứ điều gì khác (ngoại trừ trích dẫn) - vui lòng xem phần mới tôi đã thêm vào đáy.
Tôi sử dụng một tập lệnh perl nhỏ, mà tôi gọi là "unort":
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
Tôi cũng đã có một phiên bản được phân định bằng NULL, được gọi là "unsort0" ... tiện dụng để sử dụng với find -print0, v.v.
PS: Được bình chọn là 'shuf', tôi không biết rằng có trong coreutils những ngày này ... những điều trên có thể vẫn hữu ích nếu hệ thống của bạn không có 'shuf'.
<STDIN>
bằng <>
để làm cho giải pháp hoạt động với đầu vào từ các tập tin quá.
Đây là lần thử đầu tiên dễ dàng với bộ mã hóa nhưng khó với CPU có số ngẫu nhiên cho mỗi dòng, sắp xếp chúng và sau đó tách số ngẫu nhiên từ mỗi dòng. Trong thực tế, các dòng được sắp xếp ngẫu nhiên:
cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
head myfile | awk ...
. Sau đó, tôi chỉ cần thay đổi nó thành con mèo; đó là lý do tại sao nó bị bỏ lại ở đó
-k1 -n
sắp xếp, vì đầu ra của awk rand()
là số thập phân từ 0 đến 1 và bởi vì tất cả vấn đề là nó được sắp xếp lại theo cách nào đó. -k1
có thể giúp tăng tốc nó bằng cách bỏ qua phần còn lại của dòng, mặc dù đầu ra của rand () phải đủ duy nhất để đoản mạch so sánh.
cat filename |
(hoặc < filename |
) hơn là nhớ cách mỗi chương trình lấy tệp đầu vào (hoặc không).
đây là một kịch bản 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
đầu ra
$ 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
với sort
và cut
. Đối với không quá vài nghìn dòng, nó không tạo ra nhiều sự khác biệt, nhưng với số dòng cao hơn thì nó có vấn đề (ngưỡng phụ thuộc vào việc awk
triển khai được sử dụng). Một đơn giản hóa nhỏ sẽ là để thay thế các dòng while (1){
và if (e==d) {break}
với while (e<d)
.
Một lớp lót cho trăn:
python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile
Và để in chỉ một dòng ngẫu nhiên duy nhất:
python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile
Nhưng hãy xem bài viết này để biết nhược điểm của python random.shuffle()
. Nó sẽ không hoạt động tốt với nhiều yếu tố (hơn 2080).
/dev/urandom
hiện tại. Để sử dụng nó từ Python : random.SystemRandom().shuffle(L)
.
.readLines()
trả về các dòng có dòng mới.
Hàm dựa trên awk đơn giản sẽ thực hiện công việc:
shuffle() {
awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}
sử dụng:
any_command | shuffle
Điều này sẽ làm việc trên hầu hết các UNIX. Đã thử nghiệm trên Linux, Solaris và HP-UX.
Cập nhật:
Lưu ý rằng các số 0 ( %06d
) và rand()
phép nhân hàng đầu làm cho nó hoạt động chính xác cũng trên các hệ thống sort
không hiểu số. Nó có thể được sắp xếp thông qua thứ tự từ điển (hay còn gọi là so sánh chuỗi bình thường).
"$@"
, nó cũng sẽ hoạt động với các tệp làm đầu vào. Không có lý do để nhân rand()
, bởi vì sort -n
có khả năng sắp xếp các phân số thập phân. Tuy nhiên, nên kiểm soát awk
định dạng đầu ra của định dạng, bởi vì với định dạng mặc định %.6g
, rand()
sẽ xuất ra số thỉnh thoảng theo ký hiệu số mũ . Mặc dù việc xáo trộn lên tới 1 triệu dòng là đủ trong thực tế, nhưng thật dễ dàng để hỗ trợ nhiều dòng hơn mà không phải trả nhiều tiền cho hiệu suất; ví dụ %.17f
.
sort
sẽ có thể xử lý các phân số thập phân (ngay cả với hàng nghìn dấu phân cách, như tôi vừa nhận thấy).
Ruby FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
puts ARGF.readlines.shuffle
, bạn có thể làm cho nó hoạt động với cả đối số đầu vào stdin và tên tệp.
ruby -e 'puts $<.sort_by{rand}'
- ARGF đã là một vô số, vì vậy chúng ta có thể xáo trộn các dòng bằng cách sắp xếp nó theo các giá trị ngẫu nhiên.
Một lớp lót cho Python dựa trên câu trả lời của scai , nhưng a) lấy stdin, b) làm cho kết quả có thể lặp lại với seed, c) chỉ chọn ra 200 dòng.
$ cat file | python -c "import random, sys;
random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
> 200lines.txt
Một cách đơn giản và trực quan sẽ được sử dụng shuf
.
Thí dụ:
Giả sử words.txt
như:
the
an
linux
ubuntu
life
good
breeze
Để xáo trộn các dòng, làm:
$ shuf words.txt
trong đó sẽ ném các dòng xáo trộn đến đầu ra tiêu chuẩn ; Vì vậy, bạn đã để ống nó vào một tập tin đầu ra như sau:
$ shuf words.txt > shuffled_words.txt
Một lần chạy ngẫu nhiên như vậy có thể mang lại:
breeze
the
linux
an
ubuntu
good
life
Chúng tôi có một gói để thực hiện công việc:
sudo apt-get install randomize-lines
Thí dụ:
Tạo một danh sách các số theo thứ tự và lưu nó vào 1000.txt:
seq 1000 > 1000.txt
để xáo trộn nó, chỉ cần sử dụng
rl 1000.txt
Đây là tập lệnh python mà tôi đã lưu dưới dạng rand.py trong thư mục nhà của mình:
#!/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()
Trên Mac OSX sort -R
và shuf
không có sẵn để bạn có thể đặt bí danh này trong bash_profile của mình dưới dạng:
alias shuf='python rand.py'
Nếu như tôi, bạn đã đến đây để tìm kiếm một thay thế shuf
cho macOS thì hãy sử dụng randomize-lines
.
Cài đặt randomize-lines
gói (homebrew), có rl
lệnh có chức năng tương tự 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
cung cấp shuf
nhị phân như gshuf
.
Nếu bạn đã cài đặt Scala, đây là một lớp lót để xáo trộn đầu vào:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
Hàm bash này có sự phụ thuộc tối thiểu (chỉ sắp xếp và 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
giải pháp được phân bổ riêng của OP , nhưng hiệu suất sẽ là một vấn đề với đầu vào lớn hơn; việc bạn sử dụng một $RANDOM
giá trị xáo trộn chính xác chỉ tối đa 32.768 dòng đầu vào; trong khi bạn có thể mở rộng phạm vi đó, có thể không đáng: ví dụ: trên máy của tôi, chạy tập lệnh của bạn trên 32.768 dòng đầu vào ngắn mất khoảng 1 giây, tức là khoảng 150 lần khi chạy shuf
, và khoảng 10 - 15 lần miễn là awk
giải pháp được phân bổ riêng của OP . Nếu bạn có thể dựa vào sort
sự hiện diện, awk
cũng nên có mặt ở đó.
Trong cửa sổ Bạn có thể thử tệp bó này để giúp bạn xáo trộn data.txt của bạn, Việc sử dụng mã bó là
C:\> type list.txt | shuffle.bat > maclist_temp.txt
Sau khi ban hành lệnh này, maclist_temp.txt sẽ chứa danh sách các dòng ngẫu nhiên.
Hi vọng điêu nay co ich.
Chưa được đề cập đến:
Việc sử dụng unsort
. Cú pháp (hơi hướng danh sách phát):
unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
[--identity] [--filenames[=profile]] [--separator sep] [--concatenate]
[--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null]
[--linefeed] [file ...]
msort
có thể xáo trộn theo dòng, nhưng nó thường quá mức:
seq 10 | msort -jq -b -l -n 1 -c r