निर्देशिकाओं को खोजें जिसमें कोई फ़ाइल नहीं है


58

हां, मैं अपना संगीत छांट रहा हूं। मुझे निम्नलिखित मंत्र में सब कुछ सुंदर रूप से व्यवस्थित किया गया है: /Artist/Album/Track - Artist - Title.extऔर यदि कोई मौजूद है, तो कवर अंदर बैठता है /Artist/Album/cover.(jpg|png)

मैं सभी दूसरे स्तर की निर्देशिकाओं के माध्यम से स्कैन करना चाहता हूं और उन लोगों को ढूंढना चाहता हूं जिनके पास कवर नहीं है। दूसरे स्तर तक, मेरा मतलब है कि मुझे परवाह नहीं है अगर /Britney Spears/एक cover.jpg नहीं है, लेकिन मुझे परवाह है अगर आपके /Britney Spears/In The Zone/पास एक नहीं होगा।

कवर-डाउनलोडिंग के बारे में चिंता न करें (यह मेरे लिए कल की एक मजेदार परियोजना है) मैं केवल एक विलोम ईश findउदाहरण के बारे में शानदार बैश-ईंधन के बारे में परवाह करता हूं ।


जो लोग केवल उन लापता को डाउनलोड करने में रुचि रखते हैं, जो लॉन्चपैड .net/coverlovin स्थापित कर रहे हैं और @phoibos उत्तर में "-exec/coverlovin.py {}";
Dror Cohen

जवाबों:


81

केस 1: आपको देखने के लिए सटीक फ़ाइल नाम पता है

का प्रयोग करें findसाथ test -e your_fileअगर एक फ़ाइल मौजूद है की जाँच करने के। उदाहरण के लिए, आप cover.jpgउन निर्देशिकाओं की तलाश करते हैं जो उनमें नहीं हैं:

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec test -e "{}/cover.jpg" ';' -print

हालांकि यह संवेदनशील है।

केस 2: आप अधिक लचीला बनना चाहते हैं

आप मामले के बारे में सुनिश्चित नहीं कर रहे हैं, और विस्तार हो सकता है jPg, png...

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec sh -c 'ls -1 "{}"|egrep -i -q "^cover\.(jpg|png)$"' ';' -print

व्याख्या:

  • shउपयोग करते समय पाइपिंग संभव नहीं है, इसलिए आपको प्रत्येक निर्देशिका के लिए एक शेल को स्पॉन करने की आवश्यकता हैfind
  • ls -1 "{}"आउटपुट केवल डायरेक्टरी के फ़ाइलनाम findवर्तमान में ट्रैवर्सिंग है
  • egrep(के बजाय grep) विस्तारित नियमित अभिव्यक्तियों का उपयोग करता है; -iखोज मामले को असंवेदनशील -qबनाता है , यह किसी भी आउटपुट को छोड़ देता है
  • "^cover\.(jpg|png)$"खोज पैटर्न है। इस उदाहरण में, यह उदाहरण के लिए cOver.png, Cover.JPGया से मेल खाता है cover.png.अन्यथा यह मतलब है कि यह मेल खाता फरार हो जाना चाहिए किसी भी चरित्र। ^रेखा की शुरुआत, $उसके अंत को चिह्नित करता है

उदाहरण के लिए अन्य खोज पैटर्न उदाहरण :

इस egrep -i -q "^cover\.(jpg|png)$"भाग को इसके साथ रखें:

  • egrep -i -q "cover\.(jpg|png)$": इसके अलावा मैच cd_cover.png, album_cover.JPG...
  • egrep -q "^cover\.(jpg|png)$": मेल खाता है cover.png, cover.jpgलेकिन नहीं Cover.jpg(केस संवेदनशीलता बंद नहीं हुई है)
  • egrep -iq "^(cover|front)\.jpg$": उदाहरण के लिए मेल खाता है front.jpg, Cover.JPGलेकिन नहीं Cover.PNG

इस बारे में अधिक जानकारी के लिए, नियमित अभिव्यक्तियाँ देखें


पूरी तरह से सुंदर - इस समस्या के साथ कि यह मामलों या अलग-अलग एक्सटेंशनों के बीच लेने के लिए लचीला नहीं है (मैंने वाइल्डकार्ड की कोशिश की लेकिन नो-गो)। मुझे आश्चर्य है कि अगर वहाँ एक बेहतर विकल्प है test
ओली

1
हम्म आप इस के साथ मिल सकते हैं, -exec bash -c '[[ -n $(find "{}" -iname "cover.*") ]]' \;लेकिन अनुकूलन के मामले में यह बहुत ही गंदी है। हालांकि यह काम करता है।
ओली

मैंने पाया कि आप OR प्रश्नों testके -o EXPRESSIONलिए लोड कर सकते हैं ... उदा: test -e "{}/cover.jpg" -o -e "{}/cover.png"जो पूर्ण विकसित खोज करने से बेहतर है लेकिन यह अभी भी संवेदनशील है।
ओली

मुझे ध्यान देना चाहिए कि अन्य दो समाधानों के खिलाफ इस (दो परीक्षण, मेरी अंतिम टिप्पणी के अनुसार) के प्रदर्शन की तुलना करना (कम करके देखना और ग्लोबिंग करना होगा) यह अब तक का सबसे धीमा (क्रमशः 684ms बनाम 40 सेमी और 50 सेमी) है
ओली

मूल-इन-सॉल्यूशन समाधान एक दूसरे से अधिक होता है और उन परिस्थितियों में टूट जाता है, जिनका $नाम dir नाम है (Ke $ ha, उदाहरण के लिए)।
ओली

12

सरल, यह ट्रांसपायर करता है। निम्नलिखित को कवर के साथ निर्देशिकाओं की एक सूची मिलती है और इसकी तुलना सभी दूसरे-स्तरीय निर्देशिकाओं की सूची के साथ की जाती है। दोनों "फाइलों" में दिखाई देने वाली लाइनें दबा दी जाती हैं, जिसमें उन निर्देशिकाओं की एक सूची छोड़ दी जाती है जिन्हें कवर की आवश्यकता होती है।

comm -3 \
    <(find ~/Music/ -iname 'cover.*' -printf '%h\n' | sort -u) \
    <(find ~/Music/ -maxdepth 2 -mindepth 2 -type d | sort) \
| sed 's/^.*Music\///'

हुर्रे।

टिप्पणियाँ:

  • commनिम्न तर्क निम्नानुसार हैं:

    • -1 फ़ाइल 1 के लिए अद्वितीय लाइनों को दबाएं
    • -2 फाइल 2 के लिए अद्वितीय लाइनों को दबाएं
    • -3 दोनों फ़ाइलों में दिखाई देने वाली लाइनें दबाएं
  • commकेवल फाइलें लेता है, इसलिए कूकी <(...)इनपुट विधि। यह एक वास्तविक [अस्थायी] फ़ाइल के माध्यम से सामग्री को पाइप करता है।

  • commइनपुट की जरूरत है या यह काम नहीं करता है और findकिसी भी तरह से आदेश की गारंटी नहीं देता है। यह भी अद्वितीय होना चाहिए। पहले findऑपरेशन में cover.*डुप्लिकेट प्रविष्टियों के लिए कई फाइलें मिल सकती थीं। sort -uजल्दी से एक के लिए उन नीचे ruffles। दूसरी खोज हमेशा अनूठी होने वाली है।

  • dirnamesed(एट अल) का सहारा लिए बिना फ़ाइल की डिर पाने के लिए एक आसान उपकरण है ।

  • findऔर commदोनों अपने आउटपुट के साथ थोड़ा गड़बड़ हैं। अंतिम sedचीजों को साफ करने के लिए है ताकि आप साथ रहें Artist/Album। यह आपके लिए वांछनीय हो सकता है या नहीं भी हो सकता है।


2
आपकी पहली findको संभवतः सरल बनाया जा सकता है find ~/Music/ -iname 'cover.*' -printf '%h\n', जिससे बचने की आवश्यकता है dirname। हालांकि dirnameकहीं और काम है।
टॉम

धन्यवाद @Tom, कि एक है बहुत कुछ (- दोनों "गर्म" पाता 29ms बनाम मेरा संगीत निर्देशिका पर 734ms) है कि हर जगह बाहर forking तेजी
ओली

9

यह खोजने के साथ ग्लोबिंग के साथ हल करने के लिए बहुत अच्छा है।

$ cd ... # to the directory one level above the album/artist structure

$ echo */*/*.cover   # lists all the covers

$ printf "%s\n" */*/*.cover # lists all the covers, one per line

अब मान लीजिए कि आपके पास इस अच्छी संरचना में कोई आवारा फाइलें नहीं हैं। वर्तमान निर्देशिका में केवल कलाकार उपनिर्देशिकाएँ हैं, और उनमें केवल एल्बम उपनिर्देशिकाएँ हैं। तब हम कुछ ऐसा कर सकते हैं:

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)

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

आदेश है कि करने के लिए दाहिने हाथ इनपुट पैदा करता है diff, printf "%s\n" */*, बस एलबम निर्देशिका सूचीबद्ध करता है। बायां हाथ कमांड *.coverपथ के माध्यम से पुनरावृत्त करता है और उनकी निर्देशिका नामों को प्रिंट करता है।

परीक्षण चालन:

$ find .   # let's see what we have here
.
./a
./a/b
./foo
./foo/bar
./foo/baz
./foo/baz/cover.jpg

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)
0a1,2
> a/b
> foo/bar

अहा, a/bऔर foo/barनिर्देशिका कोई नहीं है cover.jpg

कुछ टूटे हुए कोने के मामले हैं, जैसे कि डिफ़ॉल्ट रूप *से खुद को फैलता है अगर यह कुछ भी नहीं से मेल खाता है। इसे बैश के साथ संबोधित किया जा सकता है set -o nullglob


देर से जवाब के लिए क्षमा याचना। यह एक दिलचस्प विचार है लेकिन: कवर png और jpb में हो सकता है और, commक्लीनर से अधिक नहीं होगा diff?
ओली

comm -3 <(printf "%s\n" */*/cover* | sed -r 's/\/[^\/]+$//' | sort -u) <(printf "%s\n" */*)बिना किसी फुलझड़ियाँ के एक समझदार समझौता लगता है diff। हालाँकि, यह मेरे डबल-फाइंड की तुलना में थोड़ा धीमा है।
ओली

0
ls --color=never */*.txt | sed 's|/.*||' | sort -u -n > withtxt.txt
ls --color=never -d * | sort -u -n > all.txt
diff all.txt withtxt.txt

उन सभी निर्देशिकाओं को दिखाएगा जिनके पास txt फाइलें नहीं हैं।

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