क्या कोई बैश कमांड है जो फाइलों को गिनता है?


182

क्या कोई बैश कमांड है जो पैटर्न से मेल खाने वाली फ़ाइलों की संख्या को गिनता है?

उदाहरण के लिए, मैं एक निर्देशिका में सभी फाइलों की गिनती प्राप्त करना चाहता हूं जो इस पैटर्न से मेल खाती हैं: log*

जवाबों:


243

यह सरल वन-लाइनर किसी भी शेल में काम करना चाहिए, न कि केवल बैश:

ls -1q log* | wc -l

ls -1q आपको प्रति फ़ाइल एक पंक्ति देगा, भले ही उनके पास व्हॉट्सएप हो या विशेष वर्ण जैसे कि newlines।

आउटपुट को wc -l पर पाइप किया जाता है, जो लाइनों की संख्या को गिनता है।


10
मैं उपयोग नहीं करूंगा -l, क्योंकि stat(2)प्रत्येक फ़ाइल के लिए और गिनती के प्रयोजनों के लिए कुछ भी नहीं जोड़ता है।
8

12
मैं उपयोग नहीं lsकरता, क्योंकि यह एक बच्चे की प्रक्रिया बनाता है। log*शेल द्वारा विस्तारित किया जाता है, नहीं ls, इसलिए एक सरल काम echoकरेगा।
cdarke

2
यदि आपके पास रिक्त स्थान या विशेष वर्णों के साथ फ़ाइल नाम हैं तो एक गूंज को छोड़कर काम नहीं करेगा।
डैनियल

4
@AlterTross यह सच है (यह नहीं कि दक्षता मूल प्रश्न की आवश्यकता थी)। मुझे यह भी पता चला है कि -q नई लाइनों के साथ फाइलों का ध्यान रखता है, तब भी जब आउटपुट टर्मिनल नहीं है। और ये झंडे उन सभी प्लेटफार्मों और गोले द्वारा समर्थित हैं, जिन पर मैंने परीक्षण किया है। उत्तर को अपडेट करते हुए, धन्यवाद और इनपुट के लिए सांचा!
डैनियल

3
यदि logsप्रश्न में निर्देशिका में एक निर्देशिका कहा जाता है, तो उस लॉग निर्देशिका की सामग्री को भी गिना जाएगा। यह शायद जानबूझकर नहीं है।
मोगसी

54

आप इसे सुरक्षित रूप से कर सकते हैं (यानी स्थान के साथ या \nउनके नाम की फाइलों द्वारा बग नहीं किया जाएगा )

$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}

आपको सक्षम करने की आवश्यकता है nullglobताकि कोई फ़ाइल मेल न होने पर आपको सरणी*.log में शाब्दिक रूप से न मिले । ( "सेट -x" को "पूर्ववत करें" कैसे देखें ? उदाहरण के लिए इसे सुरक्षित रूप से कैसे रीसेट करें।)$logfiles


2
शायद यह स्पष्ट रूप से इंगित करता है कि यह एक बैश है
ट्रिपल

इसके अलावा, फाइनल shopt -u nullglobको छोड़ दिया जाना चाहिए यदि nullglobआप परेशान नहीं थे तो आपने शुरू किया।
ट्रिपलए

नोट: *.logबस के साथ प्रतिस्थापित *निर्देशिका की गणना करेगा। यदि आप जिन फ़ाइलों की गणना करना चाहते हैं, उनका पारंपरिक नामकरण कन्वेंशन है name.extension, तो उपयोग करें *.*
AlainD

52

यहाँ बहुत से उत्तर हैं, लेकिन कुछ को ध्यान में नहीं है

  • उन स्थानों के साथ नाम दर्ज करें, नई सुर्खियाँ, या उनमें वर्णों को नियंत्रित करें
  • हाइफ़न के साथ शुरू होने वाले फ़ाइल नाम (एक फ़ाइल नाम की कल्पना करें -l)
  • छिपी हुई फाइलें, जो एक डॉट के साथ शुरू होती हैं (यदि ग्लोब *.logइसके बजाय थाlog*
  • निर्देशिका जो ग्लोब से मेल खाती है (उदाहरण के लिए एक निर्देशिका जिसे logsमैच कहा जाता है log*)
  • खाली निर्देशिका (अर्थात परिणाम 0 है)
  • अत्यंत बड़ी निर्देशिका (उन सभी को सूचीबद्ध करने से स्मृति समाप्त हो सकती है)

यहाँ एक समाधान है जो उन सभी को संभालता है:

ls 2>/dev/null -Ubad1 -- log* | wc -l

स्पष्टीकरण:

  • -Ulsप्रविष्टियों को क्रमबद्ध नहीं करने का कारण बनता है, जिसका अर्थ है कि इसे पूरी निर्देशिका सूची को मेमोरी में लोड करने की आवश्यकता नहीं है
  • -bप्रिंट्स सी-स्टाइल नॉनोग्राफिक कैरेक्टर्स के लिए बच जाती है, जो महत्वपूर्ण रूप से नई कहानियों को छापती है \n
  • -aसभी फाइलों को प्रिंट करता है, यहां तक ​​कि छिपी हुई फाइलों (सख्ती की जरूरत नहीं है जब ग्लोब log*का अर्थ है छिपी हुई फाइलें नहीं)
  • -dनिर्देशिका की सामग्री को सूचीबद्ध करने का प्रयास किए बिना निर्देशिकाओं को प्रिंट करता है, जो कि lsसामान्य रूप से होता है
  • -1 यह सुनिश्चित करता है कि यह एक कॉलम पर है (ls पाइप से लिखते समय यह स्वचालित रूप से करता है, इसलिए यह कड़ाई से आवश्यक नहीं है)
  • 2>/dev/nullStderr को पुनर्निर्देशित करता है ताकि यदि 0 लॉग फ़ाइलें हों, तो त्रुटि संदेश को अनदेखा करें। (ध्यान दें कि इसके बजाय पूरी कामकाजी निर्देशिका को सूचीबद्ध shopt -s nullglobकरना होगा ls।)
  • wc -lयह उत्पन्न होने के रूप में निर्देशिका लिस्टिंग की खपत करता है, इसलिए lsकिसी भी समय मेमोरी का आउटपुट कभी भी मेमोरी में नहीं होता है।
  • --फ़ाइल नामों को कमांड से अलग किया --जाता है ताकि तर्क के रूप में नहीं समझा जा सके ls(मामले log*में हटा दिया गया है)

शेल फ़ाइलों की पूरी सूची में विस्तारित हो जाएगाlog* , जो बहुत अधिक फ़ाइलों के होने पर मेमोरी को समाप्त कर सकता है, इसलिए फिर इसे grep के माध्यम से चलाना बेहतर होगा:

ls -Uba1 | grep ^log | wc -l

यह अंतिम मेमोरी की बहुत अधिक मात्रा का उपयोग किए बिना फ़ाइलों की बहुत बड़ी निर्देशिकाओं को संभालता है (यद्यपि यह एक उपधारा का उपयोग करता है)। यह -dअब आवश्यक नहीं है, क्योंकि यह केवल वर्तमान निर्देशिका की सामग्री को सूचीबद्ध कर रहा है।


48

पुनरावर्ती खोज के लिए:

find . -type f -name '*.log' -printf x | wc -c

wc -cके आउटपुट में वर्णों की संख्या की गणना करेगा find, जबकि प्रत्येक परिणाम के लिए एक प्रिंट करना -printf xबताता findहै x

गैर-पुनरावर्ती खोज के लिए, यह करें:

find . -maxdepth 1 -type f -name '*.log' -printf x | wc -c

6
यहां तक ​​कि अगर आपके पास रिक्त स्थान वाली फाइलें नहीं हैं, तो भी आपकी स्क्रिप्ट के कुछ अन्य उपयोगकर्ता दुर्भावनापूर्ण रूप से नामित फ़ाइल का सामना कर सकते हैं, जिससे स्क्रिप्ट विफल हो सकती है। इसके अलावा, StackOverflow पर इसका सामना करने वाले अन्य लोगों के पास नई सुर्खियों वाली फाइलें हो सकती हैं, और नुकसान को जानने की जरूरत है।
मोगी

FYI करें यदि आप बस बाहर निकलते हैं -name '*.log'तो यह सभी फाइलों की गणना करेगा, जो कि मुझे अपने उपयोग के मामले के लिए आवश्यक थी। इसके अलावा -maxdepth ध्वज अत्यंत उपयोगी है, धन्यवाद!
स्ट्रामांडेलिक

2
यह अभी भी गलत परिणाम उत्पन्न करता है यदि उनमें नए नाम के साथ फ़ाइल नाम हैं। वर्कअराउंड के साथ आसान है find; सिर्फ शब्दशः फ़ाइल नाम के अलावा कुछ और प्रिंट करें।
ट्रिपल

8

इस प्रश्न के लिए स्वीकृत उत्तर गलत है, लेकिन मेरे पास कम प्रतिनिधि हैं इसलिए इसमें कोई टिप्पणी नहीं दी जा सकती।

इस प्रश्न का सही उत्तर मैट द्वारा दिया गया है:

shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}

स्वीकृत उत्तर के साथ समस्या यह है कि wc -l newline वर्णों की संख्या को गिनाता है, और उन्हें ''? ' 'ls -l' के आउटपुट में। इसका मतलब यह है कि जब फ़ाइल नाम में एक नया वर्ण होता है, तो स्वीकृत उत्तर कीलें होती हैं। मैंने सुझाए गए आदेश का परीक्षण किया है:

ls -l log* | wc -l

और यह त्रुटिपूर्ण रूप से 2 के मान की रिपोर्ट करता है, भले ही पैटर्न से मेल खाने वाली केवल 1 फ़ाइल हो, जिसके नाम में एक नई वर्ण रेखा होती है। उदाहरण के लिए:

touch log$'\n'def
ls log* -l | wc -l

6

यदि आपके पास बहुत सारी फाइलें हैं और आप सुरुचिपूर्ण shopt -s nullglobऔर बाश सरणी समाधान का उपयोग नहीं करना चाहते हैं, तो आप फ़ाइल के नाम को प्रिंट नहीं कर सकते हैं, जब तक कि आप फ़ाइल नाम को प्रिंट नहीं कर सकते हैं।

find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l

यह लॉग से मेल खाने वाली सभी फ़ाइलों .*को ढूंढेगा * और जो कि "नहीं नाम। *" के साथ शुरू नहीं होता है, लेकिन यह महत्वपूर्ण है, लेकिन यह ध्यान रखना महत्वपूर्ण है कि "ls" के लिए डिफ़ॉल्ट डॉट-फाइल नहीं दिखाना है, लेकिन डिफ़ॉल्ट खोजने के लिए उन्हें शामिल करने के लिए है।

यह एक सही उत्तर है, और किसी भी प्रकार के फ़ाइल नाम को संभालता है जिसे आप इस पर फेंक सकते हैं, क्योंकि फ़ाइल का नाम कभी भी कमांडों के बीच नहीं होता है।

लेकिन, shopt nullglobजवाब सबसे अच्छा जवाब है!


आपको शायद फिर से जवाब देने के बजाय अपने मूल उत्तर को अपडेट करना चाहिए।
कोडेनिन्जा

मैं का उपयोग कर लगता है findका उपयोग कर बनाम lsसमस्या के हल के लिए दो अलग-अलग तरीके हैं। findहमेशा एक मशीन पर मौजूद नहीं होता है, लेकिन lsआमतौर पर होता है,
mogsie

2
लेकिन तब लॉर्ड का एक बॉक्स जिसके पास findशायद उन सभी फैंसी विकल्पों के लिए lsभी नहीं है।
ट्रिपल

1
ध्यान दें कि यह पूरी निर्देशिका ट्री तक कैसे फैली है यदि आप -maxdepth 1
tripleee

1
ध्यान दें कि यह समाधान अपनी गिनती में छिपी निर्देशिकाओं के अंदर फाइलों की गणना करेगा। findयह डिफ़ॉल्ट रूप से करता है। यह भ्रम पैदा कर सकता है यदि कोई महसूस नहीं करता है कि एक छिपा हुआ बच्चा फ़ोल्डर है, और यह lsकुछ परिस्थितियों में उपयोग करने के लिए लाभप्रद हो सकता है, जो डिफ़ॉल्ट रूप से छिपी हुई फ़ाइलों की रिपोर्ट नहीं करता है।
MrPotatoHead

6

यहाँ इसके लिए मेरा एक लाइनर है।

 file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)

यह मुझे समझने में कुछ नासमझ लगा, लेकिन यह अच्छा है! तो set -- हमारे लिए तैयार होने के अलावा कुछ भी नहीं कर रहा है $#, कि शेल प्रोग्राम में पारित किए गए कमांड-लाइन तर्कों की संख्या
xverges

@xverges हां, "shopt -s nullglob" छिपी हुई फाइलों (.files) की गिनती नहीं करने के लिए है। सेट - स्थितीय मापदंडों (इस मामले में, फाइलों की संख्या) के संचय / सेटिंग के लिए है। और # $ स्थितीय मापदंडों की संख्या प्रदर्शित करने के लिए (फाइल गणना)।
zee

3

आप पुनरावर्ती निर्देशिकाओं के अंदर फ़ाइलों को खोजने के लिए -R विकल्प का उपयोग कर सकते हैं

ls -R | wc -l // to find all the files

ls -R | grep log | wc -l // to find the files which contains the word log

आप grep पर पैटर्न का उपयोग कर सकते हैं


3

एक महत्वपूर्ण टिप्पणी

(टिप्पणी के लिए पर्याप्त प्रतिष्ठा नहीं)

यह बगिया है :

ls -1q some_pattern | wc -l

यदि shopt -s nullglobसेट किया जाता है, तो यह सभी नियमित फ़ाइलों की संख्या को प्रिंट करता है , न कि केवल पैटर्न वाले (CentOS-8 और Cygwin पर परीक्षण किए गए)। कौन जानता है कि अन्य अर्थहीन कीड़े क्या lsहैं?

यह सही है और बहुत तेज है:

shopt -s nullglob; files=(some_pattern); echo ${#files[@]};

यह अपेक्षित काम करता है।


और दौड़ने का समय अलग है।
द सेंट: 0.006ऑन सेंटोस, और 0.083सिगविन (मामले में इसका उपयोग देखभाल के साथ किया जाता है)।
दूसरा: 0.000सेंटोस पर, और 0.003सिगविन पर।


2

आप शेल फ़ंक्शन का उपयोग करके इस तरह के कमांड को आसानी से परिभाषित कर सकते हैं। इस पद्धति के लिए किसी बाहरी कार्यक्रम की आवश्यकता नहीं होती है और यह किसी भी बच्चे की प्रक्रिया को जन्म नहीं देती है। यह खतरनाक lsपार्सिंग का प्रयास नहीं करता है और "विशेष" अक्षर (व्हाट्सएप, न्यूलाइन, बैकस्लैश और इतने पर) को ठीक से संभालता है। यह केवल शेल द्वारा प्रदत्त फ़ाइल नाम विस्तार तंत्र पर निर्भर करता है। यह कम से कम sh, bash और zsh के साथ संगत है।

नीचे दी गई पंक्ति एक फ़ंक्शन को परिभाषित करती है, countजो उन तर्कों की संख्या को प्रिंट करती है जिनके साथ इसे बुलाया गया है।

count() { echo $#; }

बस इसे वांछित पैटर्न के साथ कॉल करें:

count log*

परिणाम के सही होने के लिए जब ग्लोबिंग पैटर्न का कोई मेल नहीं है, तो शेल विकल्प nullglob(या failglob- जो कि zsh पर डिफ़ॉल्ट व्यवहार है) को समय विस्तार होने पर सेट किया जाना चाहिए। इसे इस तरह सेट किया जा सकता है:

shopt -s nullglob    # for sh / bash
setopt nullglob      # for zsh

आप जो गिनना चाहते हैं, उसके आधार पर आपको शेल विकल्प में भी रुचि हो सकती है dotglob

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

( shopt -s nullglob ; shopt -u failglob ; count log* )

यदि आप लाइटवेट सिंटैक्स को पुनर्प्राप्त करना चाहते हैं count log*, या यदि आप वास्तव में एक सब-स्पेलिंग से बचना चाहते हैं, तो आप कुछ लाइनों के साथ हैक कर सकते हैं:

# sh / bash:
# the alias is expanded before the globbing pattern, so we
# can set required options before the globbing gets expanded,
# and restore them afterwards.
count() {
    eval "$_count_saved_shopts"
    unset _count_saved_shopts
    echo $#
}
alias count='
    _count_saved_shopts="$(shopt -p nullglob failglob)"
    shopt -s nullglob
    shopt -u failglob
    count'

एक बोनस के रूप में, यह फ़ंक्शन अधिक सामान्य उपयोग का है। उदाहरण के लिए:

count a* b*          # count files which match either a* or b*
count $(jobs -ps)    # count stopped jobs (sh / bash)

PATH से कॉल करने वाले फ़ंक्शन को स्क्रिप्ट फ़ाइल (या समतुल्य सी प्रोग्राम) में बदलकर, इसे ऐसे findऔर प्रोग्राम के साथ भी बनाया जा सकता है xargs:

find "$FIND_OPTIONS" -exec count {} \+    # count results of a search

2

मैंने इस उत्तर को बहुत सोचा है, विशेष रूप से नॉट-पार्से-एलएस सामान दिया है । पहले तो मैंने कोशिश की

<चेतावनी! काम नहीं किया>
du --inodes --files0-from=<(find . -maxdepth 1 -type f -print0) | awk '{sum+=int($1)}END{print sum}'
</ चेतावनी! काम नहीं किया>

अगर वहाँ केवल एक फ़ाइल नाम की तरह काम किया था

touch $'w\nlf.aa'

लेकिन असफल रहा अगर मैंने इस तरह का नाम बनाया

touch $'firstline\n3 and some other\n1\n2\texciting\n86stuff.jpg'

मैं अंत में नीचे क्या डाल रहा हूँ के साथ आया था। नोट मैं निर्देशिका में सभी फ़ाइलों की गिनती प्राप्त करने की कोशिश कर रहा था (किसी भी उपनिर्देशिका को शामिल नहीं)। मुझे लगता है कि, यह @Mat और @Dan_Yard द्वारा उत्तर के साथ-साथ, @mogsie द्वारा निर्धारित की गई कम से कम अधिकांश आवश्यकताएँ हैं (मुझे याद नहीं है।) मुझे लगता है कि @mogsie द्वारा उत्तर सही है। लेकिन मैं हमेशा पार्सिंग से दूर रहने की कोशिश करता हूं lsजब तक कि यह एक अत्यंत विशिष्ट स्थिति न हो।

awk -F"\0" '{print NF-1}' < <(find . -maxdepth 1 -type f -print0) | awk '{sum+=$1}END{print sum}'

अधिक आसानी से:

awk -F"\0" '{print NF-1}' < \
  <(find . -maxdepth 1 -type f -print0) | \
    awk '{sum+=$1}END{print sum}'

यह विशेष रूप से फ़ाइलों के लिए एक खोज कर रहा है, एक अशक्त चरित्र के साथ आउटपुट का परिसीमन (रिक्त स्थान और लाइनफ़ीड के साथ समस्याओं से बचने के लिए), फिर शून्य वर्णों की संख्या की गिनती कर रहा है। फ़ाइलों की संख्या शून्य वर्णों की संख्या से एक कम होगी, क्योंकि अंत में एक अशक्त चरित्र होगा।

ओपी के सवाल का जवाब देने के लिए, विचार करने के लिए दो मामले हैं

1) गैर-पुनरावर्ती खोज:

awk -F"\0" '{print NF-1}' < \
  <(find . -maxdepth 1 -type f -name "log*" -print0) | \
    awk '{sum+=$1}END{print sum}'

2) पुनरावर्ती खोज। ध्यान दें कि -nameपैरामीटर के अंदर क्या है थोड़ा अलग व्यवहार (छिपी हुई फाइलें, आदि) के लिए बदलना पड़ सकता है।

awk -F"\0" '{print NF-1}' < \
  <(find . -type f -name "log*" -print0) | \
    awk '{sum+=$1}END{print sum}'

यदि कोई इस पर टिप्पणी करना चाहता है कि ये उत्तर उन लोगों की तुलना में कैसे हैं जिनका मैंने इस उत्तर में उल्लेख किया है, तो कृपया करें।


ध्यान दें, मुझे यह उत्तर प्राप्त करते समय इस विचार प्रक्रिया में मिला ।



0
ls -1 log* | wc -l

जिसका अर्थ है प्रति पंक्ति एक फ़ाइल की सूची और फिर लाइनों को गिनने के लिए पैरामीटर स्विचिंग के साथ शब्द गणना कमांड पर पाइप करें।


एलएस आउटपुट को पाइप करते समय "-1" विकल्प आवश्यक नहीं है। लेकिन अगर कोई फ़ाइल पैटर्न से मेल नहीं खाती है, तो आप ls त्रुटि संदेश छिपाना चाह सकते हैं। मेरा सुझाव है कि "ls log * 2> / dev / null | wc -l"।
जॉनमुद

डैनियल के जवाब के तहत चर्चा यहाँ भी प्रासंगिक है। यह ठीक काम करता है जब आपके पास नई निर्देशिकाओं के साथ मेल खाते निर्देशिका या फ़ाइल नाम नहीं होते हैं, लेकिन एक अच्छा उत्तर कम से कम इन सीमा स्थितियों को इंगित करना चाहिए, और एक महान उत्तर उनके पास नहीं होना चाहिए। कई कीड़े हैं क्योंकि किसी ने कोड / पेस्ट किए गए कोड को समझा नहीं था; तो कम से कम खामियों की ओर इशारा करते हुए उन्हें यह समझने में मदद मिलती है कि उन्हें क्या देखना है। (दी गई बात, कई और कीड़े होते हैं क्योंकि उन्होंने
कैविटीज को

-1

सब कुछ गिनती करने के लिए पाइप लाइन से शब्द गणना लाइन:

ls | wc -l

पैटर्न के साथ गणना करने के लिए, पहले पाइप को पकड़ें:

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