วิธีง่ายๆในการอ่านบรรทัดสุ่มจากไฟล์ในบรรทัดคำสั่ง Unix คืออะไร?


263

วิธีง่ายๆในการอ่านบรรทัดสุ่มจากไฟล์ในบรรทัดคำสั่ง Unix คืออะไร?


แต่ละบรรทัดมีความยาวคงที่หรือไม่?
Tracker1

ไม่แต่ละบรรทัดมีจำนวนอักขระที่แตกต่างกัน

คำตอบ:


383

คุณสามารถใช้shuf:

shuf -n 1 $FILE

rlนอกจากนี้ยังมียูทิลิตี้ที่เรียกว่า ใน Debian มันอยู่ในrandomize-linesแพ็คเกจที่ทำสิ่งที่คุณต้องการอย่างแน่นอนแม้ว่าจะไม่มีให้ใน distros ทั้งหมด ในหน้าแรกของมันแนะนำให้ใช้shufแทน (ซึ่งไม่ได้มีอยู่เมื่อมันถูกสร้างขึ้นผมเชื่อว่า) shufเป็นส่วนหนึ่งของ coreutils ของ GNU rlไม่ใช่

rl -c 1 $FILE

2
ขอบคุณสำหรับshufเคล็ดลับมันมีอยู่แล้วใน Fedora
Cheng

5
andalso, sort -Rแน่นอนจะทำให้คนรอมากถ้าจัดการกับไฟล์ขนาดใหญ่มาก - 80kk สาย - ในขณะที่shuf -nทำหน้าที่ค่อนข้างทันที
รูเบนส์

23
คุณสามารถรับ shuf บน OS X ได้โดยติดตั้งcoreutilsจาก Homebrew อาจจะเรียกว่าแทนgshuf shuf
Alyssa Ross

2
ในทำนองเดียวกันคุณสามารถใช้randomize-linesกับ OS X โดยbrew install randomize-lines; rl -c 1 $FILE
Jamie

4
โปรดทราบว่าshufเป็นส่วนหนึ่งของGNU Coreutilsดังนั้นจึงไม่จำเป็นต้องใช้ (โดยค่าเริ่มต้น) ในระบบ * BSD (หรือ Mac?) perl ซับในหนึ่ง @ Tracker1 ด้านล่างเป็นแบบพกพามากขึ้น (และจากการทดสอบของฉันจะเร็วขึ้นเล็กน้อย)
Adam Katz

74

ทางเลือกอื่น:

head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1

28
$ {RANDOM} สร้างตัวเลขน้อยกว่า 32768 ดังนั้นอย่าใช้สิ่งนี้สำหรับไฟล์ขนาดใหญ่ (เช่นพจนานุกรมภาษาอังกฤษ)
Ralf

3
สิ่งนี้ไม่ได้ให้ความน่าจะเป็นแบบเดียวกันกับคุณทุกบรรทัดเนื่องจากการทำงานของโมดูโล เรื่องนี้แทบจะไม่สำคัญเลยถ้าความยาวของไฟล์คือ << 32768 (และไม่ใช่เลยถ้ามันหารจำนวนนั้น) แต่อาจจะน่าสังเกต
Anaphory

10
คุณสามารถขยายนี้เพื่อสุ่มตัวเลข 30 (${RANDOM} << 15) + ${RANDOM}บิตโดยใช้ สิ่งนี้จะลดอคติอย่างมากและช่วยให้สามารถทำงานกับไฟล์ที่มีมากถึง 1 พันล้านบรรทัด
nneonneo

@nneonneo: เคล็ดลับเจ๋งมาก แต่ตามลิงค์นี้มันควรจะเป็น $ {RANDOM} แทนที่จะเป็น PLUS'ing stackoverflow.com/a/19602060/293064
Jay Taylor

+และ|เหมือนกันตั้งแต่${RANDOM}คือ 0..32767 ตามคำจำกัดความ
nneonneo

71
sort --random-sort $FILE | head -n 1

(ฉันชอบวิธีการ shuf ที่ดีกว่าถึงแม้ว่า - ฉันไม่รู้ด้วยซ้ำว่ามีอยู่และฉันจะไม่พบเครื่องมือนั้นด้วยตัวเอง)


10
+1 ฉันชอบ แต่คุณอาจต้องใช้ล่าสุดsortไม่ได้ทำงานกับระบบของฉัน (CentOS 5.5, Mac OS 10.7.2) นอกจากนี้การใช้แมวที่ไร้ประโยชน์สามารถลดลงได้sort --random-sort < $FILE | head -n 1
Steve Kehlet

sort -R <<< $'1\n1\n2' | head -1มีแนวโน้มที่จะส่งคืน 1 และ 2 เนื่องจากsort -Rเรียงลำดับบรรทัดที่ซ้ำกันเข้าด้วยกัน เช่นเดียวกับsort -Ruเพราะมันลบบรรทัดที่ซ้ำกัน
Lri

5
นี้ค่อนข้างช้าเนื่องจากความต้องการไฟล์ทั้งหมดจะได้รับการสับโดยก่อนท่อมันsort เลือกเส้นสุ่มจากไฟล์แทนและเร็วกว่าสำหรับฉันมาก headshuf
Bengt

1
@SteveKehlet ในขณะที่เราอยู่ที่นี่มันsort --random-sort $FILE | headจะดีที่สุดเพราะช่วยให้สามารถเข้าถึงไฟล์โดยตรงอาจทำให้การเรียงลำดับแบบคู่ขนานมีประสิทธิภาพ
WaelJ

5
--random-sortและ-Rตัวเลือกที่เฉพาะเจาะจงเพื่อ GNU sort (ดังนั้นพวกเขาจะไม่ทำงานกับ BSD หรือ Mac OS sort) การจัดเรียง GNU เรียนรู้การตั้งค่าสถานะเหล่านั้นในปี 2005 ดังนั้นคุณต้องใช้ GNU coreutils 6.0 หรือใหม่กว่า (เช่น CentOS 6)
RJHunter

31

มันง่ายมาก

cat file.txt | shuf -n 1

ได้รับนี่เป็นเพียงเล็กน้อยช้ากว่า "shuf -n 1 file.txt" ด้วยตัวเอง


2
คำตอบที่ดีที่สุด ฉันไม่รู้เกี่ยวกับคำสั่งนี้ โปรดทราบว่า-n 1ระบุ 1 บรรทัดและคุณสามารถเปลี่ยนเป็นมากกว่า 1 shufสามารถใช้กับสิ่งอื่นได้เช่นกัน ฉันเพียงแค่ส่งps auxและgrepมีการสุ่มจับกระบวนการบางส่วนที่ตรงกับชื่อ
sudo

18

perlfaq5: ฉันจะเลือกบรรทัดสุ่มจากไฟล์ได้อย่างไร นี่คืออัลกอริทึมสุ่มตัวอย่างอ่างเก็บน้ำจากหนังสืออูฐ:

perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file

นี่เป็นข้อได้เปรียบที่สำคัญในเรื่องพื้นที่ว่างมากกว่าการอ่านไฟล์ทั้งหมดคุณสามารถหาหลักฐานของวิธีนี้ได้ในศิลปะการเขียนโปรแกรมคอมพิวเตอร์เล่ม 2 หมวด 3.4.2 โดย Donald E. Knuth


1
เพื่อจุดประสงค์ในการรวม (ในกรณีที่ไซต์ที่อ้างอิงลดลง) นี่คือรหัสที่ Tracker1 ชี้ไปที่: "cat filename | perl -e 'ในขณะที่ (<>) {push (@ _, $ _);} print @ _ [แรนด์ () * @ _]; ';"
Anirvan

3
นี่คือการใช้แมวที่ไร้ประโยชน์ นี่คือการแก้ไขเล็กน้อยของรหัสที่พบใน perlfaq5 (และความอนุเคราะห์จากหนังสืออูฐ): perl -e 'srand; rand ($.) <1 && ($ line = $ _) ขณะที่ <>; พิมพ์ $ line; ' ชื่อไฟล์
Mr. Muskrat

เอาละ ... เว็บไซต์ที่เชื่อมโยงกันนั่นคือ
Nathan Fellman

ฉันเพียงแค่ benchmarked รุ่น shufN-บรรทัดของรหัสนี้กับ รหัส perl เร็วขึ้นเล็กน้อย (เร็วขึ้น 8% ตามเวลาของผู้ใช้, 24% เร็วขึ้นตามเวลาระบบ) แต่โดยทั่วไปฉันพบรหัส perl "ดูเหมือน" สุ่มน้อยกว่า (ฉันเขียนตู้เพลงใช้)
Adam Katz

2
อาหารมากขึ้นสำหรับความคิด: shufเก็บไฟล์อินพุตทั้งหมดในหน่วยความจำซึ่งเป็นความคิดที่น่ากลัวในขณะที่รหัสนี้เก็บเพียงหนึ่งบรรทัดดังนั้นข้อ จำกัด ของรหัสนี้คือจำนวนบรรทัดของ INT_MAX (2 ^ 31 หรือ 2 ^ 63 ขึ้นอยู่กับคุณ arch) สมมติว่าบรรทัดที่มีศักยภาพใด ๆ ที่เลือกนั้นเหมาะสมกับหน่วยความจำ
Adam Katz

11

ใช้สคริปต์ทุบตี:

#!/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}

1
สุ่มสามารถเป็น 0, sed ต้องการ 1 สำหรับบรรทัดแรก sed -n 0p ส่งคืนข้อผิดพลาด
asalamon74

mhm - ประมาณ $ 1 สำหรับ "tmp.txt" และ $ 2 สำหรับ NUM?
blabla999

แต่ถึงแม้จะมีข้อผิดพลาดมูลค่าจุดเพราะมันไม่จำเป็นต้อง perl หรือหลามและมีประสิทธิภาพเท่าที่คุณจะได้รับ (อ่านไฟล์สองครั้งอย่างแน่นอน แต่ไม่ได้อยู่ในหน่วยความจำ - ดังนั้นมันจะทำงานได้แม้กับไฟล์ขนาดใหญ่)
blabla999

@ asalamon74: Thanks @ blabla999: ถ้าเราสร้างฟังก์ชั่นให้ใช้งานได้ราคา $ 1 แต่ทำไมไม่คำนวณ NUM?
เปาโล Tedesco

การเปลี่ยนบรรทัด sed เป็น: head - $ {X} $ {FILE} | หาง -1 ควรจะทำมัน
JeffK

4

ทุบตีบรรทัดเดียว:

sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt

ปัญหาเล็กน้อย: ชื่อไฟล์ที่ซ้ำกัน


2
ปัญหาเล็กน้อย การดำเนินการนี้ใน / usr / share / dict / words มีแนวโน้มที่จะสนับสนุนคำที่ขึ้นต้นด้วย "A" เล่นกับมันฉันอยู่ที่ประมาณ 90% "A" คำถึง 10% "B" คำ ยังไม่มีใครขึ้นต้นด้วยตัวเลขซึ่งประกอบเป็นส่วนหัวของไฟล์
bibby

wc -l < test.txtcutหลีกเลี่ยงการต้องท่อ
fedorqui 'ดังนั้นหยุดการทำร้าย'

3

ต่อไปนี้เป็นสคริปต์ 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

1
มันใช้งานไม่ได้ มันหยุดหลังจากบรรทัดเดียว เพื่อให้ทำงานได้ฉันทำสิ่งนี้: import random, sys lines = open(sys.argv[1]).readlines() สำหรับ i ในช่วง (len (บรรทัด)): rand = random.randint (0, len (lines) -1) พิมพ์ lines.pop (rand),
Jed Daniels

ระบบแสดงความคิดเห็นโง่ ๆ พร้อมการจัดรูปแบบเส็งเคร็ง การจัดรูปแบบในความคิดเห็นไม่สามารถทำงานได้ครั้งหนึ่งใช่ไหม
Jed Daniels

รวม randint จึงlen(lines)อาจนำไปสู่ ​​IndexError print(random.choice(list(open(sys.argv[1]))))คุณสามารถใช้ นอกจากนี้ยังมีหน่วยความจำที่มีประสิทธิภาพขั้นตอนวิธีการสุ่มตัวอย่างอ่างเก็บน้ำ
jfs

2
พื้นที่ค่อนข้างหิว พิจารณาไฟล์ 3TB
Michael Campbell

@MichaelCampbell: อัลกอริทึมการสุ่มตัวอย่างอ่างเก็บน้ำที่ฉันได้กล่าวถึงข้างต้นอาจทำงานกับไฟล์ 3TB (ถ้าขนาดบรรทัดมี จำกัด )
jfs

2

อีกวิธีใช้ ' awk '

awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name

2
ที่ใช้ awk และ bash ( $RANDOMเป็นbashism ) นี่คือวิธี awk (mawk) บริสุทธิ์โดยใช้ตรรกะเดียวกับรหัส perlfaq5 ที่อ้างถึงของ @ Tracker1 ข้างต้น: awk 'rand() * NR < 1 { line = $0 } END { print line }' file.name(ว้าวมันสั้นกว่ารหัส perl ด้วยซ้ำ!)
Adam Katz

รหัสนั้นจะต้องอ่านไฟล์ ( wc) เพื่อรับจำนวนบรรทัดจากนั้นจะต้องอ่าน (ส่วนหนึ่งของ) ไฟล์อีกครั้ง ( awk) เพื่อรับเนื้อหาของหมายเลขบรรทัดสุ่มที่กำหนด I / O จะแพงกว่าการสุ่มตัวเลข รหัสของฉันอ่านไฟล์ครั้งเดียวเท่านั้น ปัญหากับ awk's rand()คือมันเริ่มจากวินาทีดังนั้นคุณจะได้รับซ้ำถ้าคุณใช้มันเร็วเกินไปอย่างต่อเนื่อง
Adam Katz

1

โซลูชันที่ใช้งานได้กับ 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ในตัวอย่างก่อนหน้า

0
#!/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]}

เนื่องจาก $ RANDOM สร้างตัวเลขน้อยกว่าจำนวนคำใน / usr / share / dict / words ซึ่งมี 235886 (สำหรับ Mac ของฉัน) ฉันเพิ่งสร้างตัวเลขสุ่ม 6 ตัวแยกระหว่าง 0 และ 9 และรวมเข้าด้วยกัน จากนั้นฉันตรวจสอบให้แน่ใจว่าตัวเลขนั้นน้อยกว่า 235886 จากนั้นลบศูนย์นำหน้าเพื่อจัดทำดัชนีคำที่ฉันเก็บไว้ในอาร์เรย์ เนื่องจากแต่ละคำเป็นบรรทัดของตัวเองจึงสามารถใช้ไฟล์ใดก็ได้เพื่อสุ่มเลือกบรรทัด
Ken

0

นี่คือสิ่งที่ฉันค้นพบเนื่องจาก 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

เสียงสะท้อนของตัวแปรคือการมองเห็นตัวเลขสุ่มที่สร้างขึ้น


0

ใช้วานิลลาเท่านั้นและ 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


ดูความคิดเห็นที่ฉันโพสต์เมื่อหนึ่งปีก่อนคำตอบนี้ซึ่งมีวิธีการแก้ปัญหา awk ที่ง่ายกว่าที่ไม่ต้องใช้ความยุ่งยาก นอกจากนี้ยังทราบข้อแม้ของฉันเกี่ยวกับตัวสร้างตัวเลขสุ่มของ awk ซึ่งมีการรวบรวมทุกวินาที
Adam Katz
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.