किसी दिए गए एक्सटेंशन के साथ सभी फाइलें ढूंढना जिसका आधार नाम मूल निर्देशिका का नाम है


9

मैं हर *.pdfफाइल को एक निर्देशिका में देखना चाहता हूं, ~/fooजिसका आधार नाम फ़ाइल की मूल निर्देशिका के नाम से मेल खाता हो।

उदाहरण के लिए, मान लीजिए कि निर्देशिका संरचना ~/fooइस तरह दिखती है

foo
├── dir1
│   ├── dir1.pdf
│   └── dir1.txt
├── dir2
│   ├── dir2.tex
│   └── spam
│       └── spam.pdf
└── dir3
    ├── dir3.pdf
    └── eggs
        └── eggs.pdf

मेरा वांछित आदेश चलाने से वापस आ जाएगी

~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf

क्या यह संभव है findया कुछ अन्य मुख्य उपयोगिता? मुझे लगता है कि यह -regexविकल्प का उपयोग करने योग्य है, findलेकिन मुझे यकीन नहीं है कि कैसे सही पैटर्न लिखना है।


हाँ, मैं अब एक उदाहरण का मज़ाक उड़ाऊँगा।
ब्रायन

1
@Inian ने एक उदाहरण जोड़ा। क्या यह मदद करता है?
ब्रायन

जवाबों:


16

GNU के साथ find:

find . -regextype egrep -regex '.*/([^/]+)/\1\.pdf'
  • -regextype egrep egrep शैली regex का उपयोग करें।
  • .*/ भव्य अभिभावक निर्देशन का मिलान करें।
  • ([^/]+)/ एक समूह में माता-पिता की उपस्थिति का मिलान करें।
  • \1\.pdfbackreferenceपैरेंट डायर के रूप में फ़ाइल नाम से मेल खाने के लिए उपयोग करें।

अपडेट करें

एक (स्वयं के लिए) यह सोच सकता है कि .*लालची पर्याप्त है, यह /माता-पिता के मिलान से बाहर करने के लिए अनावश्यक है :

find . -regextype egrep -regex '.*/(.+)/\1\.pdf'

इसके बाद के संस्करण अच्छी तरह से काम नहीं करेगा, क्योंकि यह गणित ./a/b/a/b.pdf:

  • .*/ माचिस ./
  • (.+)/ माचिस a/b/
  • \1.pdf माचिस a/b.pdf

बहुत ही शांत। काश मैं यह अच्छी तरह से regex कर सकता।
ब्रायन

या find . -regex '.*/\([^/]*\)/\1\.pdf'फिर यह बीएसडी के साथ भी काम करेगा find
स्टीफन चेज़लस

7

find .. -exec sh -c ''बेसन से मेल खाने के लिए शेल कंस्ट्रक्शन का उपयोग करने के लिए पारंपरिक लूप वेरिएंट और ऊपर का तत्काल रास्ता नीचे करना होगा।

find foo/ -name '*.pdf' -exec sh -c '
    for file; do 
        base="${file##*/}"
        path="${file%/*}"
        if [ "${path##*/}" =  "${base%.*}" ]; then
            printf "%s\n" "$file" 
        fi
    done' sh {} +

अलग-अलग पैरामीटर विस्तार को तोड़ने के लिए

  • fileइसमें कमांड .pdfसे लौटी फाइल का पूरा रास्ता शामिल हैfind
  • "${file##*/}"/फ़ाइल के पिछले नाम यानी केवल आधार के बाद केवल भाग होता है
  • "${file%/*}"/परिणाम के बेसन भाग को छोड़कर, अंतिम तक का मार्ग समाहित करता है
  • "${path##*/}"चर /से अंतिम के बाद का हिस्सा है path, यानी फ़ाइल के आधार के ऊपर तत्काल फ़ोल्डर पथ
  • "${base%.*}".pdfनिकाले गए हिस्से के साथ बेसन का हिस्सा शामिल है

तो अगर विस्तार के बिना बेसन ऊपर के तत्काल फ़ोल्डर के नाम के साथ मेल खाता है, तो हम पथ प्रिंट करते हैं।


7

इनियन के जवाब का उल्टा , यानी निर्देशिकाओं की तलाश करें, और फिर देखें कि क्या वे एक विशेष नाम के साथ एक फ़ाइल रखते हैं।

निम्न निर्देशिका के सापेक्ष मिली फ़ाइलों के पथनामों को प्रिंट करता है foo:

find foo -type d -exec sh -c '
    for dirpath do
        pathname="$dirpath/${dirpath##*/}.pdf"
        if [ -f "$pathname" ]; then
            printf "%s\n" "$pathname"
        fi
    done' sh {} +

${dirpath##*/}निर्देशिका पथ के फ़ाइल नाम भाग द्वारा प्रतिस्थापित किया जाएगा, और इसके द्वारा प्रतिस्थापित किया जा सकता है $(basename "$dirpath")

शॉर्ट-सर्किट सिंटैक्स पसंद करने वाले लोगों के लिए:

find foo -type d -exec sh -c '
    for dirpath do
        pathname="$dirpath/${dirpath##*/}.pdf"
        [ -f "$pathname" ] && printf "%s\n" "$pathname"
    done' sh {} +

इस तरह से करने का लाभ यह है कि आपके पास निर्देशिकाओं की तुलना में अधिक पीडीएफ फाइलें हो सकती हैं। यदि कोई छोटी संख्या (निर्देशिकाओं की संख्या) द्वारा क्वेरी को प्रतिबंधित करता है, तो शामिल परीक्षणों की संख्या कम हो जाती है।

उदाहरण के लिए, यदि किसी एकल निर्देशिका में 100 पीडीएफ फाइलें होती हैं, तो यह केवल निर्देशिका के खिलाफ सभी 100 फाइलों के नामों के परीक्षण के बजाय उनमें से किसी एक का पता लगाने की कोशिश करेगा।



2

यह निर्दिष्ट नहीं किया गया था, लेकिन यहां नियमित अभिव्यक्ति के बिना एक समाधान है यदि कोई दिलचस्पी है।

हम find . -type fकेवल फाइल प्राप्त करने के लिए उपयोग कर सकते हैं , फिर सशर्त लिखने dirnameऔर उपयोग basenameकरने के लिए। उपयोगिताओं में निम्नलिखित व्यवहार होता है:

$ find . -type f
./dir2/spam/spam.pdf
./dir2/dir2.tex
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./dir1/dir1.txt

basenameअंतिम के बाद सिर्फ नाम बदल देता है /:

$ for file in $(find . -type f); do basename $file; done
spam.pdf
dir2.tex
dir3.pdf
eggs.pdf
dir1.pdf
dir1.txt

dirnameफाइनल तक पूरा रास्ता देता है /:

$ for file in $(find . -type f); do dirname $file; done
./dir2/spam
./dir2
./dir3
./dir3/eggs
./dir1
./dir1

इसलिए, basename $(dirname $file)फ़ाइल की मूल निर्देशिका देता है।

$ for file in $(find . -type f); do basename $(dirname $file) ; done
spam
dir2
dir3
eggs
dir1
dir1

समाधान

सशर्त बनाने के लिए उपर्युक्त को मिलाएं "$(basename $file)" = "$(basename $(dirname $file))".pdf, उसके बाद ही प्रत्येक परिणाम को प्रिंट करें findयदि वह शर्त सही है।

$ while read file; do if [ "$(basename "$file")" = "$(basename "$(dirname "$file")")".pdf ]; then echo $file; fi done < <(find . -type f)
./dir2/spam/spam.pdf
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./Final Thesis/grits/grits.pdf
./Final Thesis/Final Thesis.pdf

उपरोक्त उदाहरण में, हमने उस मामले का इलाज करने के लिए नाम में रिक्त स्थान के साथ एक निर्देशिका / फ़ाइल जोड़ी है (टिप्पणियों में @ कुसलानंद के लिए धन्यवाद)


यह दुर्भाग्य से Final Thesis.pdf(एक स्थान के साथ) फ़ाइल नाम पर टूट जाएगा ।
Kusalananda

@ कुसलानंद फिक्स्ड।
user1717828

0

मैं बैश ग्लोबिंग, स्ट्रिंग लूप से अधिक लूप को किसी भी दिन फाइंड प्रोग्राम पर ले जाता हूं । मुझे तर्कहीन कहें, और जब यह अच्छी तरह से हो सकता है तो इस तरह के सरल कोड मेरे लिए चाल है: पठनीय और पुन: प्रयोज्य, संतोषजनक भी! इसलिए मुझे इसके संयोजन का सुझाव देने की अनुमति दें:

• बैश ग्लोबस्टार : for f in ** ; do ... ** अपने वर्तमान सत्र में ग्लोबस्टार की स्थिति की जांच करने के लिए वर्तमान निर्देशिका और सभी सबफ़ोल्डरों की प्रत्येक फ़ाइलों पर लूप्स shopt -p globstar। ग्लोबस्टार को सक्रिय करने के लिए shopt -s globstar:।

• "फ़ाइल" utlity : पीडीएफ केif [[ $(file "$f") =~ pdf ]]; then ... लिए वास्तविक फ़ाइल प्रारूप की जांच करने के लिए - केवल फ़ाइल के विस्तार के लिए परीक्षण से अधिक मजबूत

• बेसनेम, dirname : फ़ाइल नाम की निर्देशिका के नाम से तुलना करने के लिए तुरंत उसके ऊपर। basenameफ़ाइल नाम dirnameलौटाता है - संपूर्ण निर्देशिका पथ लौटाता है - दो फ़ंक्शन को केवल एक निर्देशिका से मेल खाते फ़ाइल को वापस करने के लिए संयोजित करें। मैं प्रत्येक को एक चर ( _mydir और _myf ) में रखता हूं , फिर स्ट्रिंग मिलान के लिए = ~ का उपयोग करके एक सरल परीक्षण करता हूं ।

एक सबटीलिटी: फ़ाइल नाम में किसी भी "डॉट" को हटाकर मौजूदा डायरेक्टरी जिसका शॉर्टकट भी है से मेल खाने वाले नाम से बचने के लिए "।" - मैंने वेरिएबल _myf पर डायरेक्ट स्ट्रिंग प्रतिस्थापन का इस्तेमाल किया है : ${_myf//./}- बहुत सुंदर नहीं है लेकिन यह काम करता है। सकारात्मक मिलान प्रत्येक फ़ाइल के पथ को लौटाएगा - साथ में आउटपुट से पहले वर्तमान फ़ोल्डर के पूर्ण पथ के साथ $(pwd)/:।

कोड

for f in ** ; do
  if [[ $(file "$f") =~ PDF ]]; then
    _mydir="$(basename $(dirname $f))" ; 
    _myf="$(basename $f)" ; 
    [[ "${_myf//./}" =~ "$_mydir" ]] && echo -e "$(pwd)/$f" ; 
  fi ; 
done
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.