मैं कैसे निर्देशिका में बेतरतीब से यादृच्छिक फ़ाइलों का चयन कर सकता हूं?


144

मेरे पास लगभग 2000 फाइलों के साथ एक निर्देशिका है। मैं Nया तो बैश स्क्रिप्ट या पाइप की गई कमांड की सूची का उपयोग करके फ़ाइलों का एक यादृच्छिक नमूना कैसे चुन सकता हूं ?


1
यूनिक्स और लिनक्स में भी एक अच्छा जवाब: unix.stackexchange.com/a/38344/24170
निकाना रेक्लाविक्स


जवाबों:


180

यहां एक स्क्रिप्ट है जो GNU सॉर्ट के यादृच्छिक विकल्प का उपयोग करती है:

ls |sort -R |tail -$N |while read file; do
    # Something involving $file, or you can leave
    # off the while to just get the filenames
done

कूल, सॉर्ट -R पता नहीं था; मैंने पहले bogosort का उपयोग किया था :-p
एलेक्स

5
सॉर्ट करें: अमान्य विकल्प - अधिक जानकारी के लिए R 'क्रमबद्ध करें-help' को आज़माएं।

2
उन फ़ाइलों के लिए काम नहीं करता है जिनके पास रिक्त स्थान है।
हाउसहेल्टर

यह रिक्त स्थान (पाइपलाइन प्रक्रियाओं लाइनों) के साथ फाइलों के लिए काम करना चाहिए। यह उन में newline वाले नामों के लिए काम नहीं करता है। केवल का उपयोग "$file", नहीं दिखाया गया है, रिक्त स्थान के प्रति संवेदनशील होगा।
यन वर्निअर


108

आप shuf(GNU कोरुटिल्स पैकेज से) का उपयोग कर सकते हैं । बस इसे फ़ाइल नामों की सूची फ़ीड करें और इसे यादृच्छिक क्रमचय से पहली पंक्ति को वापस करने के लिए कहें:

ls dirname | shuf -n 1
# probably faster and more flexible:
find dirname -type f | shuf -n 1
# etc..

-n, --head-count=COUNTवांछित लाइनों की संख्या वापस करने के लिए मूल्य समायोजित करें । उदाहरण के लिए 5 यादृच्छिक फ़ाइलनामों का उपयोग करें जिन्हें आप उपयोग करेंगे:

find dirname -type f | shuf -n 5

4
ओपी Nयादृच्छिक फ़ाइलों का चयन करना चाहता था , इसलिए उपयोग 1करना थोड़ा भ्रामक है।
aioobe

4
यदि आपके पास नए नाम के साथ फाइलनाम हैं:find dirname -type f -print0 | shuf -zn1
Hitechcomputergeek

5
क्या होगा अगर मुझे इन बेतरतीब ढंग से चुनी गई फ़ाइलों को किसी अन्य फ़ोल्डर में कॉपी करना होगा? इन बेतरतीब ढंग से चयनित फ़ाइलों पर संचालन कैसे करें?
ऋषभ अग्रहरि

18

यहां कुछ संभावनाएं हैं जो आउटपुट को पार्स नहीं करते हैं lsऔर जो कि उनके नाम के रिक्त स्थान और मजाकिया प्रतीकों वाली फाइलों के संबंध में 100% सुरक्षित हैं। वे सभी randfयादृच्छिक फ़ाइलों की सूची के साथ एक सरणी को आबाद करेंगे । printf '%s\n' "${randf[@]}"यदि आवश्यक हो तो यह सरणी आसानी से मुद्रित हो जाती है।

  • यह संभवतः एक ही फ़ाइल को कई बार आउटपुट करेगा, और Nपहले से ज्ञात करने की आवश्यकता है। यहाँ मैंने N = 42 को चुना।

    a=( * )
    randf=( "${a[RANDOM%${#a[@]}]"{1..42}"}" )

    यह सुविधा बहुत अच्छी तरह से प्रलेखित नहीं है।

  • यदि एन पहले से ज्ञात नहीं है, लेकिन आपको वास्तव में पिछली संभावना पसंद है, तो आप उपयोग कर सकते हैं eval। लेकिन यह बुराई है, और आपको वास्तव में सुनिश्चित करना चाहिए कि Nउपयोगकर्ता इनपुट से सीधे पूरी तरह से जाँच किए बिना नहीं आता है!

    N=42
    a=( * )
    eval randf=( \"\${a[RANDOM%\${#a[@]}]\"\{1..$N\}\"}\" )

    मैं व्यक्तिगत रूप से नापसंद करता हूं evalऔर इसलिए यह जवाब है!

  • अधिक सरल विधि (लूप) का उपयोग करके समान:

    N=42
    a=( * )
    randf=()
    for((i=0;i<N;++i)); do
        randf+=( "${a[RANDOM%${#a[@]}]}" )
    done
  • यदि आप संभवतः एक ही फ़ाइल को कई बार नहीं करना चाहते हैं:

    N=42
    a=( * )
    randf=()
    for((i=0;i<N && ${#a[@]};++i)); do
        ((j=RANDOM%${#a[@]}))
        randf+=( "${a[j]}" )
        a=( "${a[@]:0:j}" "${a[@]:j+1}" )
    done

ध्यान दें । यह एक पुरानी पोस्ट का उत्तर देने में देर है, लेकिन बाहरी पृष्ठ पर स्वीकृत उत्तर लिंक जो भयानक दिखाता हैअभ्यास, और अन्य उत्तर ज्यादा बेहतर नहीं है क्योंकि यह भी आउटपुट को पार्स करता है ls। लुनथ द्वारा एक उत्कृष्ट जवाब के लिए स्वीकृत उत्तर बिंदुओं पर एक टिप्पणी जो स्पष्ट रूप से अच्छा अभ्यास दिखाती है, लेकिन ओपी को बिल्कुल जवाब नहीं देती है।


पहले और दूसरे ने "खराब प्रतिस्थापन" का उत्पादन किया; यह "{1..42}"भाग को पीछे छोड़ते हुए पसंद नहीं आया "1"। इसके अलावा, $RANDOMकेवल 15 बिट है और चुनने के लिए 32767 से अधिक फ़ाइलों के साथ विधि काम नहीं करेगी।
यन वर्निअर

13
ls | shuf -n 10 # ten random files

1
आप के उत्पादन पर भरोसा नहीं करना चाहिए ls। यह काम नहीं करेगा जैसे उदाहरण के लिए एक फ़ाइल नाम में नई सीमाएँ हैं।
bfontaine

3
@ बॉटनटेन आप फ़ाइल नामों में नई सूची से प्रेतवाधित लगते हैं :)। क्या वे वास्तव में आम हैं? दूसरे शब्दों में, क्या कोई ऐसा उपकरण है जो उनके नाम से नई-नई फाइलों को बनाता है? चूंकि एक उपयोगकर्ता के रूप में इस तरह का फ़ाइल नाम बनाना बहुत मुश्किल है। इंटरनेट से आने वाली फ़ाइलों के लिए समान
सिप्रियन टॉमोयागै

3
@CiprianTomoiaga आपको प्राप्त हो सकने वाले मुद्दों का एक उदाहरण है। lsआपको "साफ़" फ़ाइलनाम देने की गारंटी नहीं है, इसलिए आपको इस पर भरोसा नहीं करना चाहिए। यह तथ्य कि ये मुद्दे दुर्लभ या असामान्य हैं, समस्या को नहीं बदलते हैं; विशेष रूप से इसके लिए बेहतर समाधान दिए गए हैं।
bfontaine

lsनिर्देशिका और रिक्त लाइनें शामिल हो सकती हैं। मैं find . -type f | shuf -n10इसके बजाय कुछ सुझाव देना चाहूंगा ।
२१:४

9

पार्स ls से परहेज5 करते हुए यादृच्छिक फ़ाइलों का चयन करने के लिए एक सरल समाधान । यह रिक्त स्थान, newlines और अन्य विशेष वर्णों वाली फ़ाइलों के साथ भी काम करता है:

shuf -ezn 5 * | xargs -0 -n1 echo

echoउस कमांड से बदलें जिसे आप अपनी फ़ाइलों के लिए निष्पादित करना चाहते हैं।


1
ठीक है, क्या पाइप + readमें पार्सिंग के समान समस्याएं नहीं हैं ls? अर्थात्, यह लाइन से लाइन को पढ़ता है, इसलिए यह उनके नाम की नई
लाइनों के

3
तुम सही हो। मेरे पिछले समाधान ने नए नामों वाली फ़ाइल नामों के लिए काम नहीं किया और शायद कुछ विशेष वर्णों के साथ दूसरों पर भी टूट गया। मैंने अपने उत्तर को न्यूलाइन्स के बजाय नल-समाप्ति का उपयोग करने के लिए अद्यतन किया है।
स्काइ

4

यदि आपके पास पायथन स्थापित है (पायथन 2 या पायथन 3 के साथ काम करता है):

एक फ़ाइल (या एक मनमाना कमांड से लाइन) का चयन करने के लिए, का उपयोग करें

ls -1 | python -c "import sys; import random; print(random.choice(sys.stdin.readlines()).rstrip())"

Nफ़ाइलों / लाइनों का चयन करने के लिए , उपयोग करें (नोट Nकमांड के अंत में है, इसे एक नंबर से बदलें)

ls -1 | python -c "import sys; import random; print(''.join(random.sample(sys.stdin.readlines(), int(sys.argv[1]))).rstrip())" N

यह तब काम नहीं करता है जब आपके फ़ाइल नाम में नई सुर्खियाँ हों।
bfontaine

4

यह @ gniourf_gniourf के देर से जवाब के लिए एक और बाद की प्रतिक्रिया है, जो मैंने अभी तक उत्थान किया है क्योंकि यह अब तक का सबसे अच्छा उत्तर है, दो बार से अधिक। (एक बार बचने के लिए evalऔर एक बार सुरक्षित फ़ाइल नाम से निपटने के लिए।)

लेकिन मुझे इस उत्तर का उपयोग करने में "बहुत अच्छी तरह से प्रलेखित नहीं" सुविधा को अनसुना करने में कुछ मिनट लगे। यदि आपके बैश कौशल इतने ठोस हैं कि आपने तुरंत देखा कि यह कैसे काम करता है, तो इस टिप्पणी को छोड़ दें। लेकिन मैंने ऐसा नहीं किया, और मुझे लगता है कि यह मुझे समझ में नहीं आया है।

फ़ीचर # 1 शेल की अपनी फ़ाइल ग्लोबिंग है। a=(*)एक सरणी बनाता है $a, जिसके सदस्य वर्तमान निर्देशिका में फाइलें हैं। बैश फिल्नाम के सभी अजीबों को समझता है, ताकि सूची की गारंटी सही हो, गारंटीकृत बच जाए, आदि द्वारा लौटाए गए पाठ फ़ाइल नामों को ठीक से पार्स करने के लिए चिंता करने की कोई आवश्यकता नहीं है ls

फ़ीचर # 2 सरणियों के लिए बैश पैरामीटर विस्तार है , एक दूसरे के भीतर नेस्टेड है। इसके साथ शुरू होता है , जो की लंबाई तक फैलता है ।${#ARRAY[@]}$ARRAY

उस विस्तार का उपयोग तब सरणी को सबस्क्रिप्ट करने के लिए किया जाता है। 1 और N के बीच एक यादृच्छिक संख्या खोजने का मानक तरीका यादृच्छिक संख्या modulo N का मान लेना है। हम 0 और हमारे सरणी की लंबाई के बीच एक यादृच्छिक संख्या चाहते हैं। यहाँ दृष्टिकोण, स्पष्टता के लिए दो लाइनों में टूट गया है:

LENGTH=${#ARRAY[@]}
RANDOM=${a[RANDOM%$LENGTH]}

लेकिन यह समाधान अनावश्यक चर असाइनमेंट को हटाते हुए एक ही लाइन में करता है।

फ़ीचर # 3 है बैश ब्रेस विस्तार , हालांकि मैं कबूल करने के लिए मैं पूरी तरह यह समझ में नहीं आता है। ब्रेस विस्तार उदाहरण के लिए प्रयोग किया जाता है, 25 नामित फ़ाइलों की एक सूची उत्पन्न करने के लिए filename1.txt, filename2.txt, आदि: echo "filename"{1..25}".txt"

उपर्युक्त के अंदर की अभिव्यक्ति, "${a[RANDOM%${#a[@]}]"{1..42}"}"42 अलग-अलग विस्तार का उत्पादन करने के लिए उस चाल का उपयोग करती है। ब्रेस विस्तार में ]और के बीच एक एकल अंक होता है }, जो पहले मुझे लगा कि सरणी को सबस्क्रिप्ट कर रहा है, लेकिन यदि ऐसा है तो यह एक बृहदान्त्र से पहले होगा। (यह सरणी में एक यादृच्छिक स्थान से 42 लगातार आइटम भी लौटाएगा, जो कि सरणी से 42 यादृच्छिक आइटम वापस करने के समान बिल्कुल भी नहीं है।) मुझे लगता है कि यह सिर्फ शेल को विस्तार से 42 बार चला रहा है, जिससे वापसी हो रही है। सरणी से 42 यादृच्छिक आइटम। (लेकिन अगर कोई इसे और अधिक पूरी तरह से समझा सकता है, तो मुझे यह सुनना अच्छा लगेगा।)

एन को हार्डकोड किया जाना है (42 करने के लिए) यह है कि ब्रेस विस्तार चर विस्तार से पहले होता है।

अंत में, यहाँ सुविधा # 4 है , यदि आप एक निर्देशिका पदानुक्रम के लिए पुनरावर्ती करना चाहते हैं:

shopt -s globstar
a=( ** )

एक पर यह बदल जाता है खोल विकल्प है कि कारण बनता है **रिकर्सिवली मैच के लिए। अब आपकी $aसरणी में पूरी पदानुक्रम में प्रत्येक फ़ाइल है।


2

यदि आपके पास अपने फ़ोल्डर में अधिक फाइलें हैं, तो आप नीचे दिए गए पाइप्ड कमांड का उपयोग कर सकते हैं जो मैंने यूनिक्स स्टैकएक्सचेंज में पाया है ।

find /some/dir/ -type f -print0 | xargs -0 shuf -e -n 8 -z | xargs -0 cp -vt /target/dir/

यहां मैं फ़ाइलों को कॉपी करना चाहता था, लेकिन यदि आप फ़ाइलों को स्थानांतरित करना चाहते हैं या कुछ और करना चाहते हैं, तो बस अंतिम कमांड को बदल दें जहां मैंने उपयोग किया है cp


1

यह एकमात्र स्क्रिप्ट है जिसे मैं मैकओएस पर बैश के साथ अच्छा खेल सकता हूं। मैंने निम्नलिखित दो लिंक से स्निपेट संयुक्त और संपादित किए हैं:

ls कमांड: मैं एक पुनरावर्ती पूर्ण पथ सूची, प्रति फ़ाइल एक पंक्ति कैसे प्राप्त कर सकता हूं?

http://www.linuxquestions.org/questions/linux-general-1/is-there-a-bash-command-for-picking-a-random-file-678687/

#!/bin/bash

# Reads a given directory and picks a random file.

# The directory you want to use. You could use "$1" instead if you
# wanted to parametrize it.
DIR="/path/to/"
# DIR="$1"

# Internal Field Separator set to newline, so file names with
# spaces do not break our script.
IFS='
'

if [[ -d "${DIR}" ]]
then
  # Runs ls on the given dir, and dumps the output into a matrix,
  # it uses the new lines character as a field delimiter, as explained above.
  #  file_matrix=($(ls -LR "${DIR}"))

  file_matrix=($(ls -R $DIR | awk '; /:$/&&f{s=$0;f=0}; /:$/&&!f{sub(/:$/,"");s=$0;f=1;next}; NF&&f{ print s"/"$0 }'))
  num_files=${#file_matrix[*]}

  # This is the command you want to run on a random file.
  # Change "ls -l" by anything you want, it's just an example.
  ls -l "${file_matrix[$((RANDOM%num_files))]}"
fi

exit 0

1

MacOS में सॉर्ट -R और shuf कमांड नहीं होते हैं, इसलिए मुझे एक बैश केवल समाधान की आवश्यकता थी जो बिना डुप्लिकेट के सभी फ़ाइलों को रैंडमाइज करता है और यहां वह नहीं मिला। यह समाधान gniourf_gniourf के समाधान # 4 के समान है, लेकिन उम्मीद है कि बेहतर टिप्पणियाँ जोड़ता है।

स्क्रिप्ट को एन नमूने के साथ एक काउंटर का उपयोग करके एन नमूने को रोकने के बाद संशोधित करने के लिए आसान होना चाहिए, या एन $ रैंडम के साथ gniourf_gniourf का लूप ~ 32000 फाइलों तक सीमित है, लेकिन यह ज्यादातर मामलों के लिए करना चाहिए।

#!/bin/bash

array=(*)  # this is the array of files to shuffle
# echo ${array[@]}
for dummy in "${array[@]}"; do  # do loop length(array) times; once for each file
    length=${#array[@]}
    randomi=$(( $RANDOM % $length ))  # select a random index

    filename=${array[$randomi]}
    echo "Processing: '$filename'"  # do something with the file

    unset -v "array[$randomi]"  # set the element at index $randomi to NULL
    array=("${array[@]}")  # remove NULL elements introduced by unset; copy array
done

0

मैं इसका उपयोग करता हूं: यह अस्थायी फ़ाइल का उपयोग करता है लेकिन एक निर्देशिका में गहराई तक जाता है जब तक कि यह एक नियमित फ़ाइल नहीं ढूंढता है और इसे वापस नहीं करता है।

# find for a quasi-random file in a directory tree:

# directory to start search from:
ROOT="/";  

tmp=/tmp/mytempfile    
TARGET="$ROOT"
FILE=""; 
n=
r=
while [ -e "$TARGET" ]; do 
    TARGET="$(readlink -f "${TARGET}/$FILE")" ; 
    if [ -d "$TARGET" ]; then
      ls -1 "$TARGET" 2> /dev/null > $tmp || break;
      n=$(cat $tmp | wc -l); 
      if [ $n != 0 ]; then
        FILE=$(shuf -n 1 $tmp)
# or if you dont have/want to use shuf:
#       r=$(($RANDOM % $n)) ; 
#       FILE=$(tail -n +$(( $r + 1 ))  $tmp | head -n 1); 
      fi ; 
    else
      if [ -f "$TARGET"  ] ; then
        rm -f $tmp
        echo $TARGET
        break;
      else 
        # is not a regular file, restart:
        TARGET="$ROOT"
        FILE=""
      fi
    fi
done;

-1

श्री कांग से थोड़ा सा समाधान के बारे में यहाँ पर कैसे सूचित किया जाए:
मैं यूनिक्स कमांड लाइन पर या शेल स्क्रिप्ट में टेक्स्ट फ़ाइल की लाइनों को कैसे फेरबदल कर सकता हूं?

$ ls | perl -MList :: Util = shuffle -e '@lines = shuffle (<>); प्रिंट करें @lines [0..4] '

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.