सभी उपनिर्देशिकाओं में फ़ाइलों की संख्या की रिपोर्ट कैसे करें?


24

मुझे सभी उप-निर्देशिकाओं का निरीक्षण करने और रिपोर्ट करने की आवश्यकता है कि उनमें कितनी फाइलें (बिना और पुनरावृत्ति के) हैं:

directoryName1 numberOfFiles
directoryName2 numberOfFiles

findजब आप बैश करेंगे तब आप क्यों इस्तेमाल करना चाहते हैं? (shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done): सभी निर्देशिकाओं के लिए, उस निर्देशिका में प्रविष्टियों की संख्या (छिपी हुई डॉट फाइलें, छोड़कर .और ..) सहित
गिनमॉस्टन

@janmoesen आपने ऐसा क्यों नहीं किया? मैं शेल स्क्रिप्टिंग के लिए नया हूं, लेकिन मैं आपके विधि के साथ कोई भी गोच नहीं देख सकता। मेरे लिए, यह सबसे अच्छा तरीका है। किसी ने भी आपकी टिप्पणी को गलत नहीं ठहराया है, लेकिन किसी ने भी यह टिप्पणी नहीं की है कि यह बुरा भी हो सकता है। उत्कीर्ण उत्तरों में आपके मुकाबले अधिक प्रतिनिधि होते हैं इसलिए यह मुझे आश्चर्यचकित करता है कि क्या मुझे कुछ याद आ रहा है।
टॉक्सालॉट

@toxalot: मैंने इसे एक उत्तर के रूप में जोड़ने की जहमत नहीं उठाई क्योंकि यह बहुत छोटा था (और संभवतः स्वर में थोड़ा कृपालु)। बेझिझक टिप्पणी को उभारें। :-) इसके अलावा, सवाल कुछ अस्पष्ट है कि "कितनी फाइलें" का अर्थ है। मेरा समाधान "नियमित" फाइलों और निर्देशिकाओं को गिनता है ; शायद पोस्टर का मतलब वास्तव में "फाइलें, निर्देशिका नहीं" था। एक और बात का ध्यान रखें कि यह ग्लोबिंग "छिपी हुई" डॉट फाइलों को ध्यान में नहीं रखता है। उन दोनों गोचरों के आस-पास के रास्ते हैं, हालाँकि। लेकिन फिर से: मूल पोस्टर की सटीक आवश्यकताओं के बारे में निश्चित नहीं।
14:56

जवाबों:


30

यह एक सुरक्षित और पोर्टेबल तरीके से करता है। यह अजीब फ़ाइल नाम से भ्रमित नहीं होगा।

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 {} \;। मैं केवल फाइलों को टैली करने के उद्देश्य से एक नई लाइन प्रिंट करना चाहता हूं।


1
दुनिया में कोई ऐसा व्यक्ति क्यों है जो फ़ाइल नाम में नई सुर्खियों का उपयोग करता है? जवाब के लिए धन्यवाद।
श्योबॉय

1
फिल्नाम में किसी भी वर्ण को छोड़कर / और अशक्त वर्ण हो सकते हैं, मेरा मानना ​​है। dwheeler.com/essays/fixing-unix-linux-filenames.html
फ्लिम्स

2
गणना में निर्देशिका ही शामिल होगी। आप बाहर रखना है कि गिनती, प्रयोग से चाहते हैं-mindepth 1
toxalot

तुम भी इस्तेमाल कर सकते हैं -printf '\n'के बजाय -exec echo
टॉक्सालॉट

1
@toxalot आप कर सकते हैं यदि आपके पास एक ऐसा पता है जो समर्थन करता है -printf, लेकिन यदि आप नहीं चाहते कि यह FreeBSD पर काम करे, उदाहरण के लिए।
शॉन जे। गोफ

6

यह मानते हुए कि आप एक मानक लिनक्स समाधान की तलाश कर रहे हैं, इसे प्राप्त करने का एक अपेक्षाकृत सरल तरीका है find:

find dir1/ dir2/ -maxdepth 1 -type f | wc -l

जहाँ 1 के findलिए दो निर्दिष्ट उपनिर्देशिका का पता लगाता है, -maxdepthजो आगे की पुनरावृत्ति को रोकता है और केवल रिपोर्ट ( -type f) को नए सिरे से अलग करता है। फिर wcउन पंक्तियों की संख्या गिनने के लिए परिणाम को पाइप किया जाता है।


मेरे पास 2 से अधिक dirs हैं ... मैं find . -maxdepth 1 -type dआउटपुट के साथ आपकी कमांड कैसे जोड़ सकता हूं ?
श्योबॉय

आप या तो (ए) एक चर में आवश्यक निर्देशिकाओं को शामिल कर सकते हैं और find $dirs ...(बी) यदि वे विशेष रूप से एक उच्च स्तर की निर्देशिका में हैं, तो उस निर्देशिका से ग्लोब,find */ ...
जसोवरीयन

1
यह गलत परिणामों की सूचना देगा यदि किसी फ़ाइल नाम में कोई नया वर्ण है।
शॉन जे। गोफ

@ शॉन: धन्यवाद। मैंने सोचा था कि मैं रिक्त स्थान के साथ फ़ाइल नाम था, लेकिन नई लाइनों पर विचार नहीं किया था: एक तय करने के लिए कोई सुझाव?
जसोनव्रीयन

-exec echoअपने खोज कमांड में जोड़ें - इस तरह यह फ़ाइल नाम, सिर्फ एक नई पंक्ति को प्रतिध्वनित नहीं करता है।
शॉन जे। गोफ

5

"पुनरावृत्ति के बिना" से, क्या आपका मतलब है कि यदि 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)।


2

एक गिनती स्क्रिप्ट के आधार पर , शॉन का जवाब और यह सुनिश्चित करने के लिए एक नई लाइन के साथ फिल्नाम भी एक ही लाइन पर प्रयोग करने योग्य रूप में मुद्रित किए जाते हैं:

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आदेश बस प्रत्येक फ़ाइल के लिए एक एकल वर्ण मुद्रण, और उसके बाद उनकी गिनती लाइनों के बजाय उन की गणना के द्वारा काम करता है।


1

यहाँ एक "जानवर बल" -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}'

इस काम। लेकिन आउटपुट गड़बड़ है अगर आपके पास नाम में `` स्थान है।
ShyBoy

यह गलत परिणामों की सूचना देगा यदि किसी फ़ाइल नाम में कोई नया वर्ण है।
शॉन जे। गोफ

-1
for i in *; do echo $i; ls $i | wc -l; done

4
U & L में आपका स्वागत है। स्पष्टीकरण के साथ उत्तर लंबे रूप में होने चाहिए और केवल कोड ड्रॉप नहीं। कृपया इसका विस्तार करें और बताएं कि क्या चल रहा है। इसके अलावा यह ऐसा करने के लिए एक बहुत ही अक्षम तरीका है और उदाहरण के लिए, रिक्त स्थान के साथ फ़ाइलों को संभालना नहीं है।
स्लम

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