यूनिक्स कमांड लाइन में फ़ाइल से यादृच्छिक रेखा पढ़ने का एक आसान तरीका क्या है?


263

यूनिक्स कमांड लाइन में फ़ाइल से यादृच्छिक रेखा पढ़ने का एक आसान तरीका क्या है?


क्या प्रत्येक रेखा एक निश्चित लंबाई तक गद्देदार होती है?
ट्रैकर 1

नहीं, प्रत्येक पंक्ति में वर्णों की चर संख्या है

जवाबों:


383

आप उपयोग कर सकते हैं shuf:

shuf -n 1 $FILE

एक उपयोगिता भी कहा जाता है rl। डेबियन में यह randomize-linesपैकेज में वही होता है जो आप चाहते हैं, हालांकि सभी डिस्ट्रो में उपलब्ध नहीं है। अपने होम पेज पर यह वास्तव में shufबदले के उपयोग की सिफारिश करता है (जो तब नहीं था जब इसे बनाया गया था, मुझे विश्वास है)। shufGNU कोरुटिल्स का हिस्सा है, rlनहीं है।

rl -c 1 $FILE

2
shufटिप के लिए धन्यवाद , यह फेडोरा में बनाया गया है।
चेंग

5
अंडालो, sort -Rनिश्चित रूप से एक बहुत प्रतीक्षा करने जा रहा है अगर बहुत बड़ी फ़ाइलों के साथ काम कर रहा है - 80kk लाइनें -, जबकि, shuf -nतुरंत काम करता है।
रुबेंस

23
आप coreutilsHomebrew से इंस्टॉल करके OS X पर shuf कर सकते हैं । के gshufबजाय बुलाया जा सकता है shuf
एलिसा रॉस

2
इसी तरह, आप randomize-linesओएस एक्स बायbrew install randomize-lines; rl -c 1 $FILE
जेमी

4
ध्यान दें कि GNU Coreutilsshuf का हिस्सा है और इसलिए आवश्यक रूप से * BSD सिस्टम (या Mac) पर उपलब्ध (डिफ़ॉल्ट रूप से) उपलब्ध नहीं होगा। @ Tracker1 का पर्ल वन-लाइनर अधिक पोर्टेबल है (और मेरे परीक्षणों से, थोड़ा तेज है)।
एडम काटज़

74

एक अन्य विकल्प:

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

28
$ {RANDOM} केवल 32768 से कम संख्या उत्पन्न करता है, इसलिए बड़ी फ़ाइलों के लिए इसका उपयोग न करें (उदाहरण के लिए अंग्रेज़ी शब्दकोश)।
राल्फ

3
यह आपको हर लाइन के लिए सटीक समान संभावना नहीं देता है, मोडुलो ऑपरेशन के कारण। यह मुश्किल से तब होता है जब फ़ाइल की लंबाई << 32768 है (और बिल्कुल नहीं अगर यह उस संख्या को विभाजित करता है), लेकिन शायद ध्यान देने योग्य है।
एनाफरी

10
आप इसका उपयोग करके 30-बिट यादृच्छिक संख्या तक बढ़ा सकते हैं (${RANDOM} << 15) + ${RANDOM}। यह पूर्वाग्रह को काफी कम करता है और इसे 1 बिलियन लाइनों तक की फ़ाइलों के लिए काम करने की अनुमति देता है।
nnonneo

@nneonneo: बहुत ही बढ़िया ट्रिक, हालाँकि इस लिंक के अनुसार यह OR'ing $ {RANDOM} का होना चाहिए, इसके बजाय PLUS'ing stackoverflow.com/a/19602060/293064
Jay Taylor

+और परिभाषा के अनुसार 0..32767 |से समान हैं ${RANDOM}
nnonneo

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
स्टीव केहलेट

sort -R <<< $'1\n1\n2' | head -11 और 2 की वापसी की संभावना है, क्योंकि sort -Rडुप्लिकेट लाइनें एक साथ होती हैं। वही लागू होता है sort -Ru, क्योंकि यह डुप्लिकेट लाइनों को हटा देता है।
11

5
यह अपेक्षाकृत धीमी गति से है, क्योंकि पूरी फ़ाइल को sortपाइप करने से पहले इसमें फेरबदल करने की आवश्यकता होती है headshufफ़ाइल से यादृच्छिक रेखाओं का चयन करता है, इसके बजाय और मेरे लिए बहुत तेज है।
Bengt

1
@SteveKehlet जब हम इस पर हैं, sort --random-sort $FILE | headतो यह सबसे अच्छा होगा, क्योंकि यह सीधे फ़ाइल को एक्सेस करने की अनुमति देता है, संभवतः कुशल समानांतर छँटाई को सक्षम करता है
WaelJ

5
--random-sortऔर -Rविकल्पों जीएनयू प्रकार के लिए विशिष्ट हैं (ताकि वे बीएसडी या मैक ओएस के साथ काम नहीं करेगा sort)। GNU सॉर्ट ने उन झंडों को 2005 में सीखा ताकि आपको GNU कोरुटिल्स 6.0 या नए (जैसे CentOS 6) की आवश्यकता हो।
RJHunter

31

यह सरल है।

cat file.txt | shuf -n 1

दी यह केवल अपने दम पर "shuf -n 1 file.txt" की तुलना में एक धीमी गति है।


2
सबसे बढ़िया उत्तर। मैं इस कमांड के बारे में नहीं जानता था। ध्यान दें कि -n 11 पंक्ति को निर्दिष्ट करता है, और आप इसे 1 से अधिक में बदल सकते हैं। shufअन्य चीजों के लिए भी इस्तेमाल किया जा सकता है; मैंने अभी-अभी पाइप किया ps auxऔर इसके grepसाथ बेतरतीब ढंग से एक नाम से मेल खाते प्रक्रियाओं को मार डाला।
sudo

18

perlfaq5: मैं किसी फ़ाइल से यादृच्छिक रेखा कैसे चुनूं? यहाँ कैमल बुक से एक जलाशय-नमूना एल्गोरिथ्म है:

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

इसमें पूरी फाइल को पढ़ने के स्थान पर एक महत्वपूर्ण लाभ है। आप डोनाल्ड ई। नुथ द्वारा, द आर्ट ऑफ़ कंप्यूटर प्रोग्रामिंग, वॉल्यूम 2, धारा 3.4.2 में इस पद्धति का प्रमाण पा सकते हैं।


1
शामिल किए जाने के प्रयोजनों के लिए (यदि निर्दिष्ट साइट नीचे जाती है), तो यहां उस कोड को दिखाया गया है, जो Tracker1 ने इंगित किया है: "cat filename। Perl -e 'जबकि (<>) {push (@ _, $ _);} प्रिंट ... _ [रैंड () * @ _]; '; "
अनिरवन

3
यह बिल्ली का बेकार उपयोग है। यहाँ perlfaq5 (और कैमल बुक के सौजन्य से) में पाया गया कोड का एक मामूली संशोधन है: perl -e 'srand; रैंड ($।) <1 && ($ लाइन = $ _) जबकि <>; $ लाइन प्रिंट करें? ' फ़ाइल नाम
श्री मस्कट

गलत ... लिंक की गई साइट, वह है
नाथन फ़ेलमैन

मैंने अभी-अभी इस कोड के एन-लाइन्स संस्करण के खिलाफ बेंचमार्क किया था shuf। पर्ल कोड बहुत थोड़ा तेज है (उपयोगकर्ता समय के अनुसार 8% तेज, सिस्टम समय के अनुसार 24% तेज), हालांकि वास्तविक रूप से मैंने पाया है कि पर्ल कोड "कम यादृच्छिक" है (मैंने इसका उपयोग करके एक ज्यूकबॉक्स लिखा है)।
एडम काटज़

2
विचार के लिए अधिक भोजन: shufमेमोरी में पूरे इनपुट फ़ाइल को संग्रहीत करता है , जो एक भयानक विचार है, जबकि यह कोड केवल एक पंक्ति को संग्रहीत करता है, इसलिए इस कोड की सीमा INT_MAX की एक पंक्ति गणना है (2 ^ 31 या 2 ^ 63 आपके आधार पर आर्क), इसकी किसी भी चयनित संभावित रेखा को मानकर स्मृति में फिट बैठता है।
एडम काटज

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 हो सकता है, पहली पंक्ति के लिए सेड 1 की जरूरत है। sed -n 0p रिटर्न त्रुटि।
asalamon74

mhm - "tmp.txt" के लिए $ 1 और NUM के लिए $ 2 कैसे?
ब्लाबला 999

लेकिन यहां तक ​​कि एक बिंदु के बग के साथ, क्योंकि इसमें पर्ल या अजगर की जरूरत नहीं है और यह उतना ही कुशल है जितना आप प्राप्त कर सकते हैं (फ़ाइल को दो बार नहीं बल्कि मेमोरी में पढ़ सकते हैं - इसलिए यह बड़ी फ़ाइलों के साथ भी काम करेगा)।
ब्लाबला 999

@ asalamon74: धन्यवाद @ blabla999: यदि हम इससे एक फंक्शन बनाते हैं, तो $ 1 के लिए ठीक है, लेकिन एमयूएम की गणना क्यों नहीं?
पाओलो टेडेस्को

सीड लाइन को इसमें बदलना: हेड - $ {X} $ {FILE} | टेल -1 को करना चाहिए
जेएफके

4

सिंगल बैश लाइन:

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

थोड़ा समस्या: डुप्लिकेट फ़ाइल नाम।


2
मंदी की समस्या। इस पर / usr / शेयर / तानाशाह / शब्दों का प्रदर्शन "A" से शुरू होने वाले शब्दों का पक्ष लेता है। इसके साथ खेलते हुए, मैं लगभग 90% "ए" शब्दों से 10% "बी" शब्दों में हूं। संख्याओं के साथ कोई भी अभी तक शुरू नहीं हुआ है, जो फ़ाइल का सिर बनाते हैं।
bibby

wc -l < test.txtपाइप करने से बचा जाता है cut
फेडोरक्वी 'एसओ ने

3

यहाँ एक सरल पायथन स्क्रिप्ट है जो काम करेगी:

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 for रेंज (len (लाइनें)): rand = random.randint (0, len (लाइनें) -1) प्रिंट लाइन्स। Pop (रैंड),
जेड डेनियल

भद्दी टिप्पणी के साथ मूर्ख टिप्पणी प्रणाली। टिप्पणियों में प्रारूपण एक समय में एक बार काम नहीं किया?
जैद डेनियल

रैंडिंट समावेशी है इसलिए IndexError को len(lines)जन्म दे सकता है। आप उपयोग कर सकते हैं print(random.choice(list(open(sys.argv[1]))))। स्मृति कुशल जलाशय नमूना एल्गोरिथ्म भी है
19

2
काफी जगह भूखा; एक 3TB फ़ाइल पर विचार करें।
माइकल कैंपबेल

@ मिचेलकैम्पबेल: जलाशय का नमूनाकरण एल्गोरिथ्म जिसका मैंने ऊपर उल्लेख किया है, 3TB फ़ाइल (यदि लाइन का आकार सीमित है) के साथ काम कर सकता है।
1

2

' Awk ' का उपयोग करने का दूसरा तरीका

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

2
यह awk और bash ( bashism ) का उपयोग करता $RANDOMहै । यहाँ एक शुद्ध awk (mawk) विधि है जो @ Tracker1 के उद्धृत perlfaq5 कोड के समान तर्क का उपयोग कर रही है: (वाह, यह पर्ल कोड से भी कम है!)awk 'rand() * NR < 1 { line = $0 } END { print line }' file.name
एडम काटज़

उस कोड को wcलाइन की गिनती प्राप्त करने के लिए फ़ाइल ( ) को पढ़ना चाहिए, फिर awkदिए गए यादृच्छिक लाइन नंबर की सामग्री को प्राप्त करने के लिए फ़ाइल को फिर से ( ) का हिस्सा पढ़ना होगा । रैंडम नंबर प्राप्त करने की तुलना में I / O कहीं अधिक महंगा होगा। मेरा कोड केवल एक बार फाइल पढ़ता है। Awk के साथ मुद्दा rand()यह है कि यह सेकंड पर आधारित है, इसलिए यदि आप इसे बहुत तेजी से चलाते हैं तो आपको डुप्लिकेट मिलेंगे।
एडम काटज़

1

एक समाधान जो MacOSX पर भी काम करता है, और लिनक्स (?) पर भी काम करना चाहिए:

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 / शेयर / तानाशाह / शब्दों से कम संख्याएँ उत्पन्न होती हैं, जिसमें 235886 (वैसे भी मेरे मैक पर) हैं, मैं सिर्फ 0 और 9 के बीच 6 अलग-अलग यादृच्छिक संख्याएँ उत्पन्न करता हूँ और उन्हें एक साथ स्ट्रिंग करता हूँ। फिर मैं यह सुनिश्चित करता हूं कि संख्या 235886 से कम हो। फिर उन शब्दों को अनुक्रमित करने के लिए अग्रणी शून्य हटा दें जिन्हें मैंने सरणी में संग्रहीत किया है। चूँकि प्रत्येक शब्द की अपनी एक पंक्ति होती है इसलिए इसे आसानी से किसी भी फाइल के लिए बेतरतीब ढंग से एक लाइन चुनने के लिए इस्तेमाल किया जा सकता है।
केन

0

यहाँ मैं क्या खोज रहा हूँ क्योंकि मेरा मैक ओएस सभी आसान उत्तरों का उपयोग नहीं करता है। मैंने एक संख्या उत्पन्न करने के लिए 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

केवल वेनिला sed और awk का उपयोग करना, और $ RANDOM का उपयोग किए बिना, एक सरल, अंतरिक्ष-कुशल और यथोचित उपवास "वन-लाइनर" के लिए एक लाइन छद्म-यादृच्छिक रूप से FILENAME नामक फ़ाइल से चयन करने के लिए निम्नानुसार है:

sed -n $(awk 'END {srand(); r=rand()*NR; if (r<NR) {sub(/\..*/,"",r); r++;}; print r}' FILENAME)p FILENAME

(यह तब भी काम करता है, जब FILENAME खाली हो, जिस स्थिति में कोई रेखा उत्सर्जित नहीं होती है।)

इस दृष्टिकोण का एक संभावित लाभ यह है कि यह केवल एक बार रैंड () कहता है।

जैसा कि @AdamKatz ने टिप्पणियों में बताया है, एक और संभावना प्रत्येक पंक्ति के लिए रैंड () को कॉल करने की होगी:

awk 'rand() * NR < 1 { line = $0 } END { print line }' FILENAME

(इंडक्शन के आधार पर शुद्धता का एक सरल प्रमाण दिया जा सकता है।)

के बारे में बताएं rand()

"ज्यादातर awk कार्यान्वयन में, जिसमें gawk, रैंड () एक ही शुरुआती संख्या या बीज से संख्या उत्पन्न करना शुरू करता है, हर बार जब आप awk चलाते हैं।"

- https://www.gnu.org/software/gawk/manual/html_node/Numeric-Functions.html


देखें टिप्पणी मैं इस जवाब से पहले एक साल तैनात , एक सरल awk समाधान है कि sed की आवश्यकता होती है नहीं करता है जो। इसके अलावा जाग के यादृच्छिक संख्या जनरेटर के बारे में मेरे चेतावनी पर ध्यान दें, जो पूरे सेकंड में बीज करता है।
एडम काट्ज़
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.