उपनिर्देशिकाओं की सूची प्राप्त करें जिसमें एक फ़ाइल होती है जिसका नाम एक स्ट्रिंग होता है


45

मैं उन उपनिर्देशिकाओं की सूची कैसे प्राप्त कर सकता हूं जिनमें एक फ़ाइल है जिसका नाम एक विशेष पैटर्न से मेल खाता है?

अधिक विशेष रूप से, मैं उन निर्देशिकाओं की तलाश कर रहा हूं जिनमें फ़ाइल नाम में कहीं न कहीं 'f' अक्षर के साथ एक फ़ाइल है।

आदर्श रूप से, सूची में डुप्लिकेट नहीं होंगे और केवल फ़ाइल नाम के बिना ही पथ शामिल होगा।

जवाबों:


43
find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort |uniq

उपरोक्त सभी फाइलें वर्तमान निर्देशिका ( .) के नीचे की सभी फाइलें ढूंढती हैं जो नियमित फाइलें ( -type f) हैं और fउनके नाम में कहीं हैं ( -name '*f*')। अगला, sedकेवल डायरेक्टरी नाम को छोड़कर, फ़ाइल नाम को हटा देता है। फिर, निर्देशिका की सूची को सॉर्ट किया गया है ( sort) और डुप्लिकेट हटा दिए गए ( uniq)।

sedआदेश एक भी विकल्प के होते हैं। यह नियमित अभिव्यक्ति के लिए मैचों की तलाश करता है /[^/]+$और किसी भी चीज के साथ मेल खाने वाले की जगह लेता है। डॉलर चिह्न का अर्थ है पंक्ति का अंत। [^/]+'एक या अधिक वर्णों का अर्थ है जो स्लैश नहीं हैं। इस प्रकार, /[^/]+$अंतिम स्लैश से पंक्ति के अंत तक सभी वर्णों का अर्थ है। दूसरे शब्दों में, यह पूर्ण पथ के अंत में फ़ाइल नाम से मेल खाता है। इस प्रकार, sed कमांड फाइल नाम को हटा देती है, जिससे फाइल में मौजूद डायरेक्टरी का नाम अपरिवर्तित रह जाता है।

सरलीकरण

कई आधुनिक sortकमांड एक -uध्वज का समर्थन करते हैं जो uniqअनावश्यक बनाता है । GNU sed के लिए:

find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort -u

और, MacOS sed के लिए:

find . -type f -name '*f*' | sed -E 's|/[^/]+$||' |sort -u

साथ ही, यदि आपकी findकमांड इसका समर्थन करती है, तो findडायरेक्ट्री नामों को सीधे प्रिंट करना संभव है । इस के लिए की जरूरत से बचा जाता है sed:

find . -type f -name '*f*' -printf '%h\n' | sort -u

अधिक मजबूत संस्करण (GNU टूल की आवश्यकता है)

उपर्युक्त संस्करण फ़ाइल नामों से भ्रमित होंगे, जिनमें नईलाइन शामिल हैं। एनयूएल-टर्मिनेटेड स्ट्रिंग्स पर छंटनी करने के लिए एक अधिक मजबूत समाधान है:

find . -type f -name '*f*' -printf '%h\0' | sort -zu | sed -z 's/$/\n/'

मेरे पास बहुत सारी फाइलें हैं जो उन सभी को बहुत महंगा बनाती हैं। uniqमिश्रण में फेंकने से बार-बार एक दूसरे के बगल में पहले से मौजूद लाइनों को हटाकर बहुत मदद मिलती है। find . -type f -name '*f*' -printf '%h\0' | uniq -z | sort -zu | tr '\0' '\n'। या यदि आपके उपकरण थोड़े पुराने हैं, तो uniq में -z विकल्प नहीं हो सकता है। find . -type f -name '*f*' -printf '%h\n' | uniq | sort -u
1851 में jbo5112

1
MacOS उपयोगकर्ता: सेड फ्लैग -r नहीं है। किसी कारण से इसके -ई
डेविड

@ दाविद बहुत सच्चा है। -EMacOS के लिए दिखाने के लिए अपडेट किया गया उत्तर ।
जॉन 1024

22

क्यों नहीं यह कोशिश करें:

find / -name '*f*' -printf "%h\n" | sort -u

सबसे बढ़िया उत्तर। पूरी तरह से POSIX- संगत, ऊपर दिए गए कुछ उत्तरों के विपरीत, और विशेष सबसे छोटी पाइपलाइन पुरस्कार :) भी अर्जित करता है ।
किमी किमी

मैं किसी को इस बनाम दूसरों के समय को दिखाने के लिए प्यार करूंगा, क्योंकि मुझे लगता है कि यह अब तक का सबसे तेज है।
dlamblin

4
@ किमी मैं मानता हूं कि यह सबसे अच्छा समाधान है, लेकिन POSIX चश्माfind वास्तव में काफी विरल हैं- -printfऑपरेटर निर्दिष्ट नहीं है। यह बीएसडी के साथ काम नहीं करता है find। तो, नहीं "पूरी तरह से POSIX संगत।" (हालांकि sort -u POSIX में है ।)
वाइल्डकार्ड

8

अनिवार्य रूप से 2 विधियां हैं जो आप ऐसा करने के लिए उपयोग कर सकते हैं। एक स्ट्रिंग को पार्स करेगा जबकि दूसरा प्रत्येक फ़ाइल पर काम करेगा। स्ट्रिंग को पार्स करने से उपकरण का उपयोग होता है grep, जैसे sed, या awkस्पष्ट रूप से तेज़ होने वाला है , लेकिन यहां एक उदाहरण दोनों को दिखा रहा है, साथ ही साथ आप 2 तरीकों को "प्रोफ़ाइल" कैसे कर सकते हैं।

नमूना डेटा

नीचे दिए गए उदाहरणों के लिए हम निम्नलिखित डेटा का उपयोग करेंगे

$ touch dir{1..3}/dir{100..112}/file{1..5}
$ touch dir{1..3}/dir{100..112}/nile{1..5}
$ touch dir{1..3}/dir{100..112}/knife{1..5}

इनमें से कुछ *f*फ़ाइलें हटाएं dir1/*:

$ rm dir1/dir10{0..2}/*f*

# 1 दृष्टिकोण - तार के माध्यम से पार्स करना

यहाँ हम निम्नलिखित उपकरणों का उपयोग करने, जा रहे हैं find, grepऔर sort

$ find . -type f -name '*f*' | grep -o "\(.*\)/" | sort -u | head -5
./dir1/dir103/
./dir1/dir104/
./dir1/dir105/
./dir1/dir106/
./dir1/dir107/

# 2 दृष्टिकोण - फ़ाइलों का उपयोग करके पार्स करना

पहले की तरह समान उपकरण श्रृंखला, इस समय को छोड़कर हम dirnameइसके बजाय का उपयोग करेंगे grep

$ find . -type f -name '*f*' -exec dirname {} \; | sort -u | head -5
./dir1/dir103
./dir1/dir104
./dir1/dir105
./dir1/dir106
./dir1/dir107

नोट: उपरोक्त उदाहरण head -5केवल इन उदाहरणों के साथ काम कर रहे आउटपुट की मात्रा को सीमित करने के लिए उपयोग कर रहे हैं। वे आपकी पूरी सूची प्राप्त करने के लिए सामान्य रूप से हटा दिए जाएंगे!

परिणामों की तुलना करना

हम time2 दृष्टिकोणों पर एक नज़र डालने के लिए उपयोग कर सकते हैं ।

dirname

real        0m0.372s
user        0m0.028s
sys         0m0.106s

ग्रेप

real        0m0.012s
user        0m0.009s
sys         0m0.007s

इसलिए यदि संभव हो तो स्ट्रिंग्स से निपटने के लिए हमेशा सबसे अच्छा है।

वैकल्पिक स्ट्रिंग पार्सिंग विधियाँ

grep और PCRE

$ find . -type f -name '*f*' | grep  -oP '^.*(?=/)' | sort -u

sed

$ find . -type f -name '*f*' | sed 's#/[^/]*$##' | sort -u

awk

$ find . -type f -name '*f*' | awk -F'/[^/]*$' '{print $1}' | sort -u

+1 क्योंकि यह काम करता है, लेकिन दिलचस्प रूप से यह @ जॉन 1024 के उत्तर की तुलना में कई गुना अधिक समय लेता है
Muhd

@Muhd - हाँ dirname को कॉल धीमा है। मैं एक विकल्प पर काम कर रहा हूं।
slm


1

यह जवाब बेशर्मी से स्लम जवाब पर आधारित है। यह एक दिलचस्प दृष्टिकोण था, लेकिन इसकी एक सीमा है यदि फ़ाइल और / या निर्देशिका नामों में विशेष वर्ण (स्थान, अर्ध-स्तंभ ...) था। एक अच्छी आदत है इस्तेमाल करने की find /somewhere -print0 | xargs -0 someprogam

नमूना डेटा

नीचे दिए गए उदाहरणों के लिए हम निम्नलिखित डेटा का उपयोग करेंगे

mkdir -p dir{1..3}/dir\ {100..112}
touch dir{1..3}/dir\ {100..112}/nile{1..5}
touch dir{1..3}/dir\ {100..112}/file{1..5}
touch dir{1..3}/dir\ {100..112}/kni\ fe{1..5}

इनमें से कुछ *f*फ़ाइलें हटाएं dir1/*/:

rm dir1/dir\ 10{0..2}/*f*

# 1 दृष्टिकोण - फ़ाइलों का उपयोग करके पार्स करना

$ find -type f -name '*f*' -print0 | sed -e 's#/[^/]*\x00#\x00#g' | sort -zu | xargs -0 -n1 echo | head -n5
./dir1/dir 103
./dir1/dir 104
./dir1/dir 105
./dir1/dir 106
./dir1/dir 107

नोट : उपरोक्त उदाहरण head -5केवल इन उदाहरणों के साथ काम कर रहे आउटपुट की मात्रा को सीमित करने के लिए उपयोग कर रहे हैं। वे आपकी पूरी सूची प्राप्त करने के लिए सामान्य रूप से हटा दिए जाएंगे! इसके अलावा, echoजो भी कमांड आप उपयोग करना चाहते हैं उसे बदलें ।


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