วิธีง่ายๆในการอ่านบรรทัดสุ่มจากไฟล์ในบรรทัดคำสั่ง Unix คืออะไร?
วิธีง่ายๆในการอ่านบรรทัดสุ่มจากไฟล์ในบรรทัดคำสั่ง Unix คืออะไร?
คำตอบ:
คุณสามารถใช้shuf
:
shuf -n 1 $FILE
rl
นอกจากนี้ยังมียูทิลิตี้ที่เรียกว่า ใน Debian มันอยู่ในrandomize-lines
แพ็คเกจที่ทำสิ่งที่คุณต้องการอย่างแน่นอนแม้ว่าจะไม่มีให้ใน distros ทั้งหมด ในหน้าแรกของมันแนะนำให้ใช้shuf
แทน (ซึ่งไม่ได้มีอยู่เมื่อมันถูกสร้างขึ้นผมเชื่อว่า) shuf
เป็นส่วนหนึ่งของ coreutils ของ GNU rl
ไม่ใช่
rl -c 1 $FILE
shuf
เคล็ดลับมันมีอยู่แล้วใน Fedora
sort -R
แน่นอนจะทำให้คนรอมากถ้าจัดการกับไฟล์ขนาดใหญ่มาก - 80kk สาย - ในขณะที่shuf -n
ทำหน้าที่ค่อนข้างทันที
coreutils
จาก Homebrew อาจจะเรียกว่าแทนgshuf
shuf
randomize-lines
กับ OS X โดยbrew install randomize-lines; rl -c 1 $FILE
shuf
เป็นส่วนหนึ่งของGNU Coreutilsดังนั้นจึงไม่จำเป็นต้องใช้ (โดยค่าเริ่มต้น) ในระบบ * BSD (หรือ Mac?) perl ซับในหนึ่ง @ Tracker1 ด้านล่างเป็นแบบพกพามากขึ้น (และจากการทดสอบของฉันจะเร็วขึ้นเล็กน้อย)
ทางเลือกอื่น:
head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
(${RANDOM} << 15) + ${RANDOM}
บิตโดยใช้ สิ่งนี้จะลดอคติอย่างมากและช่วยให้สามารถทำงานกับไฟล์ที่มีมากถึง 1 พันล้านบรรทัด
+
และ|
เหมือนกันตั้งแต่${RANDOM}
คือ 0..32767 ตามคำจำกัดความ
sort --random-sort $FILE | head -n 1
(ฉันชอบวิธีการ shuf ที่ดีกว่าถึงแม้ว่า - ฉันไม่รู้ด้วยซ้ำว่ามีอยู่และฉันจะไม่พบเครื่องมือนั้นด้วยตัวเอง)
sort
ไม่ได้ทำงานกับระบบของฉัน (CentOS 5.5, Mac OS 10.7.2) นอกจากนี้การใช้แมวที่ไร้ประโยชน์สามารถลดลงได้sort --random-sort < $FILE | head -n 1
sort -R <<< $'1\n1\n2' | head -1
มีแนวโน้มที่จะส่งคืน 1 และ 2 เนื่องจากsort -R
เรียงลำดับบรรทัดที่ซ้ำกันเข้าด้วยกัน เช่นเดียวกับsort -Ru
เพราะมันลบบรรทัดที่ซ้ำกัน
sort
เลือกเส้นสุ่มจากไฟล์แทนและเร็วกว่าสำหรับฉันมาก head
shuf
sort --random-sort $FILE | head
จะดีที่สุดเพราะช่วยให้สามารถเข้าถึงไฟล์โดยตรงอาจทำให้การเรียงลำดับแบบคู่ขนานมีประสิทธิภาพ
--random-sort
และ-R
ตัวเลือกที่เฉพาะเจาะจงเพื่อ GNU sort (ดังนั้นพวกเขาจะไม่ทำงานกับ BSD หรือ Mac OS sort
) การจัดเรียง GNU เรียนรู้การตั้งค่าสถานะเหล่านั้นในปี 2005 ดังนั้นคุณต้องใช้ GNU coreutils 6.0 หรือใหม่กว่า (เช่น CentOS 6)
มันง่ายมาก
cat file.txt | shuf -n 1
ได้รับนี่เป็นเพียงเล็กน้อยช้ากว่า "shuf -n 1 file.txt" ด้วยตัวเอง
-n 1
ระบุ 1 บรรทัดและคุณสามารถเปลี่ยนเป็นมากกว่า 1 shuf
สามารถใช้กับสิ่งอื่นได้เช่นกัน ฉันเพียงแค่ส่งps aux
และgrep
มีการสุ่มจับกระบวนการบางส่วนที่ตรงกับชื่อ
perlfaq5: ฉันจะเลือกบรรทัดสุ่มจากไฟล์ได้อย่างไร นี่คืออัลกอริทึมสุ่มตัวอย่างอ่างเก็บน้ำจากหนังสืออูฐ:
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
นี่เป็นข้อได้เปรียบที่สำคัญในเรื่องพื้นที่ว่างมากกว่าการอ่านไฟล์ทั้งหมดคุณสามารถหาหลักฐานของวิธีนี้ได้ในศิลปะการเขียนโปรแกรมคอมพิวเตอร์เล่ม 2 หมวด 3.4.2 โดย Donald E. Knuth
shuf
N-บรรทัดของรหัสนี้กับ รหัส perl เร็วขึ้นเล็กน้อย (เร็วขึ้น 8% ตามเวลาของผู้ใช้, 24% เร็วขึ้นตามเวลาระบบ) แต่โดยทั่วไปฉันพบรหัส perl "ดูเหมือน" สุ่มน้อยกว่า (ฉันเขียนตู้เพลงใช้)
shuf
เก็บไฟล์อินพุตทั้งหมดในหน่วยความจำซึ่งเป็นความคิดที่น่ากลัวในขณะที่รหัสนี้เก็บเพียงหนึ่งบรรทัดดังนั้นข้อ จำกัด ของรหัสนี้คือจำนวนบรรทัดของ INT_MAX (2 ^ 31 หรือ 2 ^ 63 ขึ้นอยู่กับคุณ arch) สมมติว่าบรรทัดที่มีศักยภาพใด ๆ ที่เลือกนั้นเหมาะสมกับหน่วยความจำ
ใช้สคริปต์ทุบตี:
#!/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}
ทุบตีบรรทัดเดียว:
sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt
ปัญหาเล็กน้อย: ชื่อไฟล์ที่ซ้ำกัน
wc -l < test.txt
cut
หลีกเลี่ยงการต้องท่อ
ต่อไปนี้เป็นสคริปต์ Python แบบง่ายที่จะทำงาน:
import random, sys
lines = open(sys.argv[1]).readlines()
print(lines[random.randrange(len(lines))])
การใช้งาน:
python randline.py file_to_get_random_line_from
import random, sys lines = open(sys.argv[1]).readlines()
สำหรับ i ในช่วง (len (บรรทัด)): rand = random.randint (0, len (lines) -1) พิมพ์ lines.pop (rand),
len(lines)
อาจนำไปสู่ IndexError print(random.choice(list(open(sys.argv[1]))))
คุณสามารถใช้ นอกจากนี้ยังมีหน่วยความจำที่มีประสิทธิภาพขั้นตอนวิธีการสุ่มตัวอย่างอ่างเก็บน้ำ
อีกวิธีใช้ ' awk '
awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name
wc
) เพื่อรับจำนวนบรรทัดจากนั้นจะต้องอ่าน (ส่วนหนึ่งของ) ไฟล์อีกครั้ง ( awk
) เพื่อรับเนื้อหาของหมายเลขบรรทัดสุ่มที่กำหนด I / O จะแพงกว่าการสุ่มตัวเลข รหัสของฉันอ่านไฟล์ครั้งเดียวเท่านั้น ปัญหากับ awk's rand()
คือมันเริ่มจากวินาทีดังนั้นคุณจะได้รับซ้ำถ้าคุณใช้มันเร็วเกินไปอย่างต่อเนื่อง
โซลูชันที่ใช้งานได้กับ MacOSX และควรใช้กับ Linux (?):
N=5
awk 'NR==FNR {lineN[$1]; next}(FNR in lineN)' <(jot -r $N 1 $(wc -l < $file)) $file
ที่ไหน:
N
คือจำนวนบรรทัดสุ่มที่คุณต้องการ
NR==FNR {lineN[$1]; next}(FNR in lineN) file1 file2
-> บันทึกหมายเลขบรรทัดที่เขียนfile1
และพิมพ์บรรทัดที่สอดคล้องกันfile2
jot -r $N 1 $(wc -l < $file)
-> วาดN
ตัวเลขแบบสุ่ม ( -r
) อยู่ในช่วงที่มี(1, number_of_line_in_file)
jot
การทดแทนกระบวนการ<()
จะทำให้ดูเหมือนไฟล์สำหรับล่ามดังนั้นfile1
ในตัวอย่างก่อนหน้า#!/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]}
นี่คือสิ่งที่ฉันค้นพบเนื่องจาก Mac OS ของฉันไม่ได้ใช้คำตอบง่าย ๆ ทั้งหมด ฉันใช้คำสั่ง jot เพื่อสร้างตัวเลขเนื่องจากโซลูชันตัวแปร $ RANDOM ดูเหมือนจะไม่สุ่มในการทดสอบของฉัน เมื่อทดสอบโซลูชันของฉันฉันมีความแปรปรวนในโซลูชันที่ให้ไว้ในเอาต์พุต
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
เสียงสะท้อนของตัวแปรคือการมองเห็นตัวเลขสุ่มที่สร้างขึ้น
ใช้วานิลลาเท่านั้นและ awk และโดยไม่ต้องใช้ $ RANDOM "หนึ่งซับ" ที่เรียบง่ายประหยัดพื้นที่และรวดเร็วพอสมควรสำหรับการเลือกบรรทัดเดียวหลอก - สุ่มจากไฟล์ชื่อ FILENAME มีดังนี้:
sed -n $(awk 'END {srand(); r=rand()*NR; if (r<NR) {sub(/\..*/,"",r); r++;}; print r}' FILENAME)p FILENAME
(ใช้งานได้แม้ว่า FILENAME จะว่างเปล่าซึ่งในกรณีนี้จะไม่มีการปล่อยบรรทัด)
ข้อดีอย่างหนึ่งที่เป็นไปได้ของวิธีนี้คือจะเรียกใช้ rand () เพียงครั้งเดียวเท่านั้น
ตามที่ระบุโดย @AdamKatz ในความคิดเห็นความเป็นไปได้อีกอย่างหนึ่งคือการเรียก rand () สำหรับแต่ละบรรทัด:
awk 'rand() * NR < 1 { line = $0 } END { print line }' FILENAME
(สามารถพิสูจน์หลักฐานความถูกต้องได้ง่าย ๆ ตามการเหนี่ยวนำ)
rand()
"ในการใช้งาน awk ส่วนใหญ่รวมถึง gawk, rand () เริ่มสร้างตัวเลขจากหมายเลขเริ่มต้นเดียวกันหรือเมล็ดในแต่ละครั้งที่คุณเรียกใช้ awk"
- https://www.gnu.org/software/gawk/manual/html_node/Numeric-Functions.html