Cách dễ dàng để đọc dòng ngẫu nhiên từ một tệp trong dòng lệnh Unix là gì?
Cách dễ dàng để đọc dòng ngẫu nhiên từ một tệp trong dòng lệnh Unix là gì?
Câu trả lời:
Bạn có thể sử dụng shuf
:
shuf -n 1 $FILE
Ngoài ra còn có một tiện ích gọi là rl
. Trong Debian, nó nằm trong randomize-lines
gói thực hiện chính xác những gì bạn muốn, mặc dù không có sẵn trong tất cả các bản phát hành. Trên trang chủ của nó, nó thực sự khuyên bạn nên sử dụng shuf
thay thế (điều này không tồn tại khi nó được tạo ra, tôi tin vậy). shuf
là một phần của lõi GNU, rl
thì không.
rl -c 1 $FILE
shuf
boa, nó được tích hợp sẵn trong Fedora.
sort -R
chắc chắn sẽ khiến người ta phải chờ đợi rất nhiều nếu xử lý các tệp khổng lồ đáng kể - dòng 80kk -, trong khi đó, shuf -n
hoạt động khá tức thời.
coreutils
từ Homebrew. Có thể được gọi gshuf
thay vì shuf
.
randomize-lines
trên OS X bởibrew install randomize-lines; rl -c 1 $FILE
shuf
là một phần của GNU Coreutils và do đó không nhất thiết phải có sẵn (theo mặc định) trên các hệ thống * BSD (hoặc Mac?). Một lớp lót bên dưới của @ Tracker1 dễ di chuyển hơn (và theo thử nghiệm của tôi, nhanh hơn một chút).
Một cách khác:
head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
(${RANDOM} << 15) + ${RANDOM}
. Điều này làm giảm đáng kể độ lệch và cho phép nó hoạt động đối với các tệp chứa tới 1 tỷ dòng.
+
và |
giống nhau vì ${RANDOM}
là 0..32767 theo định nghĩa.
sort --random-sort $FILE | head -n 1
(Tôi thích cách tiếp cận shuf ở trên thậm chí còn tốt hơn - tôi thậm chí còn không biết rằng nó tồn tại và tôi sẽ không bao giờ tự mình tìm thấy công cụ đó)
sort
, không hoạt động trên bất kỳ hệ thống nào của tôi (CentOS 5.5, Mac OS 10.7.2). Ngoài ra, việc sử dụng mèo vô dụng, có thể được giảm xuốngsort --random-sort < $FILE | head -n 1
sort -R <<< $'1\n1\n2' | head -1
có khả năng trả về 1 và 2, vì sort -R
sắp xếp các dòng trùng lặp với nhau. Điều tương tự áp dụng cho sort -Ru
, bởi vì nó loại bỏ các dòng trùng lặp.
sort
trước khi đưa nó vào head
. shuf
thay vào đó chọn các dòng ngẫu nhiên từ tệp và nhanh hơn nhiều đối với tôi.
sort --random-sort $FILE | head
sẽ là tốt nhất, vì nó cho phép nó truy cập trực tiếp vào tệp, có thể cho phép sắp xếp song song hiệu quả
--random-sort
và -R
dành riêng cho sắp xếp GNU (vì vậy chúng sẽ không hoạt động với BSD hoặc Mac OS sort
). GNU sort đã học các cờ đó vào năm 2005, do đó bạn cần GNU coreutils 6.0 hoặc mới hơn (ví dụ: CentOS 6).
Cái này đơn giản.
cat file.txt | shuf -n 1
Cấp điều này chỉ là một chút chậm hơn so với "shuf -n 1 file.txt" của riêng mình.
-n 1
chỉ định 1 dòng và bạn có thể thay đổi nó thành nhiều hơn 1. shuf
cũng có thể được sử dụng cho những thứ khác; Tôi chỉ đường ống ps aux
và grep
với nó để giết ngẫu nhiên các quá trình khớp một phần tên.
perlfaq5: Làm cách nào để chọn một dòng ngẫu nhiên từ một tệp? Đây là một thuật toán lấy mẫu hồ chứa từ Sách lạc đà:
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
Điều này có một lợi thế đáng kể về không gian so với việc đọc toàn bộ tệp. Bạn có thể tìm thấy bằng chứng về phương pháp này trong Nghệ thuật lập trình máy tính, Tập 2, Mục 3.4.2, của Donald E. Knuth.
shuf
. Mã perl nhanh hơn một chút (nhanh hơn 8% theo thời gian của người dùng, nhanh hơn 24% theo thời gian hệ thống), mặc dù vậy, tôi đã tìm thấy mã perl "có vẻ" ít ngẫu nhiên hơn (tôi đã viết một máy hát tự động sử dụng nó).
shuf
lưu trữ toàn bộ tệp đầu vào trong bộ nhớ , đó là một ý tưởng khủng khiếp, trong khi mã này chỉ lưu trữ một dòng, do đó giới hạn của mã này là số lượng dòng INT_MAX (2 ^ 31 hoặc 2 ^ 63 tùy thuộc vào vòm), giả sử bất kỳ dòng tiềm năng nào được chọn của nó phù hợp với bộ nhớ.
sử dụng tập lệnh bash:
#!/bin/bash
# replace with file to read
FILE=tmp.txt
# count number of lines
NUM=$(wc - l < ${FILE})
# generate random number in range 0-NUM
let X=${RANDOM} % ${NUM} + 1
# extract X-th line
sed -n ${X}p ${FILE}
Dòng bash đơn:
sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt
Vấn đề nhẹ: tên tập tin trùng lặp.
wc -l < test.txt
tránh phải có đường ống đến cut
.
Đây là một kịch bản Python đơn giản sẽ thực hiện công việc:
import random, sys
lines = open(sys.argv[1]).readlines()
print(lines[random.randrange(len(lines))])
Sử dụng:
python randline.py file_to_get_random_line_from
import random, sys lines = open(sys.argv[1]).readlines()
cho tôi trong phạm vi (len (dòng)): rand = Random.randint (0, len (dòng) -1) in dòng.pop (rand),
len(lines)
có thể dẫn đến IndexError. Bạn có thể sử dụng print(random.choice(list(open(sys.argv[1]))))
. Ngoài ra còn có thuật toán lấy mẫu hồ chứa hiệu quả bộ nhớ .
Một cách khác sử dụng ' awk '
awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name
wc
) để có được số dòng, sau đó phải đọc lại (một phần) tệp ( awk
) để lấy nội dung của số dòng ngẫu nhiên đã cho. I / O sẽ đắt hơn nhiều so với việc lấy một số ngẫu nhiên. Mã của tôi chỉ đọc tệp một lần. Vấn đề với awk rand()
là nó tạo hạt dựa trên giây, do đó bạn sẽ nhận được các bản sao nếu bạn chạy liên tiếp quá nhanh.
Một giải pháp cũng hoạt động trên MacOSX và cũng nên hoạt động trên Linux (?):
N=5
awk 'NR==FNR {lineN[$1]; next}(FNR in lineN)' <(jot -r $N 1 $(wc -l < $file)) $file
Ở đâu:
N
là số dòng ngẫu nhiên bạn muốn
NR==FNR {lineN[$1]; next}(FNR in lineN) file1 file2
-> lưu số dòng được viết file1
và sau đó in dòng tương ứngfile2
jot -r $N 1 $(wc -l < $file)
-> vẽ N
số ngẫu nhiên ( -r
) trong phạm vi (1, number_of_line_in_file)
với jot
. Sự thay thế quá trình <()
sẽ làm cho nó trông giống như một tệp cho trình thông dịch, vì vậy file1
trong ví dụ trước.#!/bin/bash
IFS=$'\n' wordsArray=($(<$1))
numWords=${#wordsArray[@]}
sizeOfNumWords=${#numWords}
while [ True ]
do
for ((i=0; i<$sizeOfNumWords; i++))
do
let ranNumArray[$i]=$(( ( $RANDOM % 10 ) + 1 ))-1
ranNumStr="$ranNumStr${ranNumArray[$i]}"
done
if [ $ranNumStr -le $numWords ]
then
break
fi
ranNumStr=""
done
noLeadZeroStr=$((10#$ranNumStr))
echo ${wordsArray[$noLeadZeroStr]}
Đây là những gì tôi khám phá vì Mac OS của tôi không sử dụng tất cả các câu trả lời dễ dàng. Tôi đã sử dụng lệnh jot để tạo một số vì các giải pháp biến $ RANDOM dường như không phải là rất ngẫu nhiên trong thử nghiệm của tôi. Khi thử nghiệm giải pháp của tôi, tôi có sự khác biệt lớn trong các giải pháp được cung cấp ở đầu ra.
RANDOM1=`jot -r 1 1 235886`
#range of jot ( 1 235886 ) found from earlier wc -w /usr/share/dict/web2
echo $RANDOM1
head -n $RANDOM1 /usr/share/dict/web2 | tail -n 1
Tiếng vang của biến là để có được một hình ảnh của số ngẫu nhiên được tạo ra.
Chỉ sử dụng vanilla sed và awk, và không sử dụng $ RANDOM, một "lớp lót" đơn giản, tiết kiệm không gian và nhanh chóng hợp lý để chọn một dòng giả ngẫu nhiên từ một tệp có tên là FILENAME như sau:
sed -n $(awk 'END {srand(); r=rand()*NR; if (r<NR) {sub(/\..*/,"",r); r++;}; print r}' FILENAME)p FILENAME
(Điều này hoạt động ngay cả khi FILENAME trống, trong trường hợp đó không có dòng nào được phát ra.)
Một lợi thế có thể có của phương pháp này là nó chỉ gọi rand () một lần.
Như @AdamKatz đã chỉ ra trong các bình luận, một khả năng khác là gọi rand () cho mỗi dòng:
awk 'rand() * NR < 1 { line = $0 } END { print line }' FILENAME
(Một bằng chứng đơn giản về tính đúng đắn có thể được đưa ra dựa trên cảm ứng.)
rand()
"Trong hầu hết các triển khai awk, bao gồm gawk, rand () bắt đầu tạo số từ cùng một số bắt đầu hoặc hạt giống, mỗi khi bạn chạy awk."
- https://www.gnu.org/software/gawk/manual/html_node/Numeric-Fiances.html