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-linesgó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 shufthay thế (điều này không tồn tại khi nó được tạo ra, tôi tin vậy). shuflà một phần của lõi GNU, rlthì không.
rl -c 1 $FILE
shufboa, nó được tích hợp sẵn trong Fedora.
sort -Rchắ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 -nhoạt động khá tức thời.
coreutilstừ Homebrew. Có thể được gọi gshufthay vì shuf.
randomize-linestrên OS X bởibrew install randomize-lines; rl -c 1 $FILE
shuflà 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 -1có khả năng trả về 1 và 2, vì sort -Rsắ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.
sorttrước khi đưa nó vào head. shufthay 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 | headsẽ 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-sortvà -Rdà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 1chỉ định 1 dòng và bạn có thể thay đổi nó thành nhiều hơn 1. shufcũng có thể được sử dụng cho những thứ khác; Tôi chỉ đường ống ps auxvà grepvớ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ó).
shuflư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.txttrá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 file1và sau đó in dòng tương ứngfile2
jot -r $N 1 $(wc -l < $file)-> vẽ Nsố 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 file1trong 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