मेरे पास लगभग 2000 फाइलों के साथ एक निर्देशिका है। मैं N
या तो बैश स्क्रिप्ट या पाइप की गई कमांड की सूची का उपयोग करके फ़ाइलों का एक यादृच्छिक नमूना कैसे चुन सकता हूं ?
ls | shuf -n 5
यूनिक्स स्टैटेक्सचेंज से स्रोत
मेरे पास लगभग 2000 फाइलों के साथ एक निर्देशिका है। मैं N
या तो बैश स्क्रिप्ट या पाइप की गई कमांड की सूची का उपयोग करके फ़ाइलों का एक यादृच्छिक नमूना कैसे चुन सकता हूं ?
ls | shuf -n 5
यूनिक्स स्टैटेक्सचेंज से स्रोत
जवाबों:
यहां एक स्क्रिप्ट है जो 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
"$file"
, नहीं दिखाया गया है, रिक्त स्थान के प्रति संवेदनशील होगा।
आप 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
N
यादृच्छिक फ़ाइलों का चयन करना चाहता था , इसलिए उपयोग 1
करना थोड़ा भ्रामक है।
find dirname -type f -print0 | shuf -zn1
यहां कुछ संभावनाएं हैं जो आउटपुट को पार्स नहीं करते हैं 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 से अधिक फ़ाइलों के साथ विधि काम नहीं करेगी।
ls | shuf -n 10 # ten random files
ls
। यह काम नहीं करेगा जैसे उदाहरण के लिए एक फ़ाइल नाम में नई सीमाएँ हैं।
ls
आपको "साफ़" फ़ाइलनाम देने की गारंटी नहीं है, इसलिए आपको इस पर भरोसा नहीं करना चाहिए। यह तथ्य कि ये मुद्दे दुर्लभ या असामान्य हैं, समस्या को नहीं बदलते हैं; विशेष रूप से इसके लिए बेहतर समाधान दिए गए हैं।
ls
निर्देशिका और रिक्त लाइनें शामिल हो सकती हैं। मैं find . -type f | shuf -n10
इसके बजाय कुछ सुझाव देना चाहूंगा ।
पार्स ls से परहेज5
करते हुए यादृच्छिक फ़ाइलों का चयन करने के लिए एक सरल समाधान । यह रिक्त स्थान, newlines और अन्य विशेष वर्णों वाली फ़ाइलों के साथ भी काम करता है:
shuf -ezn 5 * | xargs -0 -n1 echo
echo
उस कमांड से बदलें जिसे आप अपनी फ़ाइलों के लिए निष्पादित करना चाहते हैं।
read
में पार्सिंग के समान समस्याएं नहीं हैं ls
? अर्थात्, यह लाइन से लाइन को पढ़ता है, इसलिए यह उनके नाम की नई
यदि आपके पास पायथन स्थापित है (पायथन 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
यह @ 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
सरणी में पूरी पदानुक्रम में प्रत्येक फ़ाइल है।
यदि आपके पास अपने फ़ोल्डर में अधिक फाइलें हैं, तो आप नीचे दिए गए पाइप्ड कमांड का उपयोग कर सकते हैं जो मैंने यूनिक्स स्टैकएक्सचेंज में पाया है ।
find /some/dir/ -type f -print0 | xargs -0 shuf -e -n 8 -z | xargs -0 cp -vt /target/dir/
यहां मैं फ़ाइलों को कॉपी करना चाहता था, लेकिन यदि आप फ़ाइलों को स्थानांतरित करना चाहते हैं या कुछ और करना चाहते हैं, तो बस अंतिम कमांड को बदल दें जहां मैंने उपयोग किया है cp
।
यह एकमात्र स्क्रिप्ट है जिसे मैं मैकओएस पर बैश के साथ अच्छा खेल सकता हूं। मैंने निम्नलिखित दो लिंक से स्निपेट संयुक्त और संपादित किए हैं:
ls कमांड: मैं एक पुनरावर्ती पूर्ण पथ सूची, प्रति फ़ाइल एक पंक्ति कैसे प्राप्त कर सकता हूं?
#!/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
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
मैं इसका उपयोग करता हूं: यह अस्थायी फ़ाइल का उपयोग करता है लेकिन एक निर्देशिका में गहराई तक जाता है जब तक कि यह एक नियमित फ़ाइल नहीं ढूंढता है और इसे वापस नहीं करता है।
# 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;
श्री कांग से थोड़ा सा समाधान के बारे में यहाँ पर कैसे सूचित किया जाए:
मैं यूनिक्स कमांड लाइन पर या शेल स्क्रिप्ट में टेक्स्ट फ़ाइल की लाइनों को कैसे फेरबदल कर सकता हूं?
$ ls | perl -MList :: Util = shuffle -e '@lines = shuffle (<>); प्रिंट करें @lines [0..4] '