मुझे सभी उप-निर्देशिकाओं का निरीक्षण करने और रिपोर्ट करने की आवश्यकता है कि उनमें कितनी फाइलें (बिना और पुनरावृत्ति के) हैं:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
मुझे सभी उप-निर्देशिकाओं का निरीक्षण करने और रिपोर्ट करने की आवश्यकता है कि उनमें कितनी फाइलें (बिना और पुनरावृत्ति के) हैं:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
जवाबों:
यह एक सुरक्षित और पोर्टेबल तरीके से करता है। यह अजीब फ़ाइल नाम से भ्रमित नहीं होगा।
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
ध्यान दें कि यह पहले फाइलों की संख्या को प्रिंट करेगा, फिर एक अलग लाइन पर निर्देशिका का नाम। यदि आप ओपी के प्रारूप को रखना चाहते हैं, तो आपको आगे प्रारूपण की आवश्यकता होगी, जैसे
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
यदि आपके पास उपनिर्देशिकाओं का एक विशिष्ट सेट है जिसमें आप रुचि रखते हैं, तो आप *
उन्हें बदल सकते हैं।
यह सुरक्षित क्यों है? (और इसलिए स्क्रिप्ट-योग्य)
फ़ाइल नाम में किसी भी वर्ण को छोड़कर हो सकता है /
। कुछ वर्ण ऐसे होते हैं जिन्हें विशेष रूप से या तो शेल या कमांड द्वारा व्यवहार किया जाता है। इनमें स्पेस, न्यूलाइन्स और डैश शामिल हैं।
for f in *
निर्माण का उपयोग करना प्रत्येक फ़ाइल नाम को प्राप्त करने का एक सुरक्षित तरीका है, चाहे उसमें कोई भी हो।
एक बार जब आपके पास एक चर में फ़ाइल नाम होता है, तो आपको अभी भी चीजों से बचना होगा find $f
। यदि $f
फ़ाइल नाम सम्मिलित है -test
, find
तो आपके द्वारा दिए गए विकल्प के बारे में शिकायत करेगा। इससे बचने का तरीका ./
नाम के सामने उपयोग करके है; इस तरह इसका एक ही अर्थ है, लेकिन यह अब डैश के साथ शुरू नहीं होता है।
न्यूलाइन्स और स्पेस भी एक समस्या है। यदि $f
फ़ाइल नाम के रूप में "हैलो, ब्वॉय" निहित है find ./$f
, है find ./hello, buddy
। आप कह रहे हैं find
को देखने के लिए ./hello,
और buddy
। यदि वे मौजूद नहीं हैं, तो यह शिकायत करेगा, और यह कभी नहीं दिखेगा ./hello, buddy
। इससे बचना आसान है - अपने चर के आसपास के उद्धरणों का उपयोग करें।
अंत में, फ़ाइल नाम में नई सूची हो सकती है, इसलिए फ़ाइल नाम की सूची में नई सूची गिनने से काम नहीं चलेगा; आपको नई फ़ाइल के साथ हर फ़ाइल नाम के लिए एक अतिरिक्त गिनती मिलेगी। इससे बचने के लिए, फ़ाइलों की सूची में नई संख्याओं की गणना न करें; इसके बजाय, एक नई फ़ाइल का प्रतिनिधित्व करने वाले नए सिरे (या किसी अन्य चरित्र) को गिनें। यही कारण है कि find
कमांड में बस -exec echo \;
और नहीं है -exec echo {} \;
। मैं केवल फाइलों को टैली करने के उद्देश्य से एक नई लाइन प्रिंट करना चाहता हूं।
-mindepth 1
-printf '\n'
के बजाय -exec echo
।
-printf
, लेकिन यदि आप नहीं चाहते कि यह FreeBSD पर काम करे, उदाहरण के लिए।
यह मानते हुए कि आप एक मानक लिनक्स समाधान की तलाश कर रहे हैं, इसे प्राप्त करने का एक अपेक्षाकृत सरल तरीका है find
:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
जहाँ 1 के find
लिए दो निर्दिष्ट उपनिर्देशिका का पता लगाता है, -maxdepth
जो आगे की पुनरावृत्ति को रोकता है और केवल रिपोर्ट ( -type f
) को नए सिरे से अलग करता है। फिर wc
उन पंक्तियों की संख्या गिनने के लिए परिणाम को पाइप किया जाता है।
find . -maxdepth 1 -type d
आउटपुट के साथ आपकी कमांड कैसे जोड़ सकता हूं ?
find $dirs ...
(बी) यदि वे विशेष रूप से एक उच्च स्तर की निर्देशिका में हैं, तो उस निर्देशिका से ग्लोब,find */ ...
-exec echo
अपने खोज कमांड में जोड़ें - इस तरह यह फ़ाइल नाम, सिर्फ एक नई पंक्ति को प्रतिध्वनित नहीं करता है।
"पुनरावृत्ति के बिना" से, क्या आपका मतलब है कि यदि directoryName1
उपनिर्देशिकाएँ हैं, तो आप उपनिर्देशिकाओं में फ़ाइलों की गिनती नहीं करना चाहते हैं? यदि हां, तो यहां बताई गई निर्देशिकाओं में सभी नियमित फ़ाइलों को गिनने का एक तरीका है:
count=0
for d in directoryName1 directoryName2; do
for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
if [ -f "$f" ]; then count=$((count+1)); fi
done
done
ध्यान दें कि -f
परीक्षण दो कार्य करता है: यह परीक्षण करता है कि क्या ऊपर दिए गए ग्लब्स में से एक द्वारा दर्ज की गई प्रविष्टि एक नियमित फ़ाइल है, और यह परीक्षण करता है कि क्या प्रविष्टि एक मैच थी (यदि कोई ग्लब्स कुछ भी मेल नहीं खाता है, तो पैटर्न is¹ के रूप में रहता है)। यदि आप दिए गए निर्देशिका में सभी प्रविष्टियों को उनके प्रकार की परवाह किए बिना गिनना चाहते हैं, तो प्रतिस्थापित -f
करें -e
।
Ksh के पास पैटर्न से मिलान करने वाली फ़ाइलों से मेल खाता है और यदि कोई फ़ाइल किसी पैटर्न से मेल नहीं खाती है तो रिक्त सूची बनाने के लिए। तो ksh में आप नियमित फाइल को इस तरह से गिन सकते हैं:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
या इस तरह से सभी फाइलें:
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
बैश के इस सरल बनाने के लिए अलग-अलग तरीके हैं। नियमित फ़ाइलें गिनने के लिए:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
सभी फाइलों को गिनने के लिए:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
हमेशा की तरह, यह zsh में और भी सरल है। नियमित फ़ाइलें गिनने के लिए:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
सभी फ़ाइलों को गिनने के (DN.)
लिए बदलें (DN)
।
¹ ध्यान दें कि प्रत्येक पैटर्न स्वयं से मेल खाता है, अन्यथा परिणाम बंद हो सकते हैं (जैसे कि यदि आप एक गिनती के साथ शुरू होने वाली फ़ाइलों की गिनती कर रहे हैं, तो आप सिर्फ for x in [0-9]*; do if [ -f "$x" ]; then …
इसलिए नहीं कर सकते क्योंकि कोई फ़ाइल नाम हो सकती है [0-9]foo
)।
एक गिनती स्क्रिप्ट के आधार पर , शॉन का जवाब और यह सुनिश्चित करने के लिए एक नई लाइन के साथ फिल्नाम भी एक ही लाइन पर प्रयोग करने योग्य रूप में मुद्रित किए जाते हैं:
for f in *
do
if [ -d "./$f" ]
then
printf %q "$f"
printf %s ' '
find "$f" -maxdepth 1 -printf x | wc -c
fi
done
printf %q
एक स्ट्रिंग के उद्धृत संस्करण को प्रिंट करना है, अर्थात, एक सिंगल-लाइन स्ट्रिंग जिसे आप बैश स्क्रिप्ट में शाब्दिक स्ट्रिंग सहित (संभावित) newlines और अन्य विशेष वर्णों के रूप में व्याख्या करने के लिए डाल सकते हैं। उदाहरण के लिए, echo -n $'\tfoo\nbar'
बनाम देखें printf %q $'\tfoo\nbar'
।
find
आदेश बस प्रत्येक फ़ाइल के लिए एक एकल वर्ण मुद्रण, और उसके बाद उनकी गिनती लाइनों के बजाय उन की गणना के द्वारा काम करता है।
यहाँ एक "जानवर बल" -ish अपने परिणाम प्राप्त करने के तरीका है, का उपयोग करते हुए find
, echo
, ls
, wc
, xargs
और awk
।
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in *; do echo $i; ls $i | wc -l; done
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
find
जब आप बैश करेंगे तब आप क्यों इस्तेमाल करना चाहते हैं?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done)
: सभी निर्देशिकाओं के लिए, उस निर्देशिका में प्रविष्टियों की संख्या (छिपी हुई डॉट फाइलें, छोड़कर.
और..
) सहित