एक निर्देशिका में डुप्लिकेट ढूंढें और निकालें


12

मेरे पास कई img फ़ाइलों के साथ एक निर्देशिका है और उनमें से कुछ समान हैं लेकिन उन सभी के अलग-अलग नाम हैं। मुझे डुप्लिकेट को निकालने की आवश्यकता है लेकिन केवल एक bashस्क्रिप्ट के साथ कोई बाहरी उपकरण नहीं । मैं लिनक्स में एक शुरुआत कर रहा हूँ। मैंने md5रकम की तुलना करने और परिणाम निकालने के आधार पर लूप के लिए नेस्टेड की कोशिश की, लेकिन सिंटैक्स के साथ कुछ गलत है और यह काम नहीं करता है। कोई मदद?

मैंने कोशिश की है ...

for i in directory_path; do
    sum1='find $i -type f -iname "*.jpg" -exec md5sum '{}' \;'
    for j in directory_path; do
        sum2='find $j -type f -iname "*.jpg" -exec md5sum '{}' \;'
        if test $sum1=$sum2 ; then rm $j ; fi
    done
done

मुझे मिला: test: too many arguments


कृपया अपने प्रश्न में कोई भी त्रुटि संदेश शामिल करें।
terdon

आप fdupes जैसे बाहरी उपकरणों का उपयोग क्यों नहीं कर सकते हैं? @terdon का उत्तर आश्चर्यजनक है, लेकिन यह वास्तव में हाइलाइट करता है कि क्यों एक अच्छा उपकरण का उपयोग करना संभव हो तो जाने का तरीका है। यदि यह किसी प्रकार का समर्पित हार्डवेयर या सर्वर है, तो आप अभी भी इसे एक नेटवर्क आदि पर एक्सेस करने में सक्षम हो सकते हैं, जिसमें एक मशीन है जिसमें fdupes जैसे उपकरण उपलब्ध हैं।
जो

जवाबों:


28

आपकी स्क्रिप्ट में कुछ समस्याएं हैं।

  • सबसे पहले, एक चर के लिए एक कमांड के परिणाम को निर्दिष्ट करने के लिए आपको इसे या तो बैकटिक्स ( `command`) में संलग्न करना होगा या, अधिमानतः $(command),। आपके पास यह एकल उद्धरण ( 'command') में है जो आपके चर के लिए आपके कमांड के परिणाम को असाइन करने के बजाय, कमांड को एक स्ट्रिंग के रूप में असाइन करता है। इसलिए, आपका testवास्तव में है:

    $ echo "test $sum1=$sum2"
    test find $i -type f -iname "*.jpg" -exec md5sum {} \;=find $j -type f -iname "*.jpg" -exec md5sum {} \;
    
  • अगला मुद्दा यह है कि कमांड md5sumकेवल हैश से अधिक वापस आता है:

    $ md5sum /etc/fstab
    46f065563c9e88143fa6fb4d3e42a252  /etc/fstab
    

    आप केवल पहले क्षेत्र की तुलना करना चाहते हैं, इसलिए आपको md5sumआउटपुट को एक कमांड के माध्यम से पास करके पार्स करना चाहिए जो केवल पहले क्षेत्र को प्रिंट करता है:

    find $i -type f -iname "*.png" -exec md5sum '{}' \; | cut -f 1 -d ' '

    या

    find $i -type f -iname "*.png" -exec md5sum '{}' \; | awk '{print $1}' 
  • इसके अलावा, findकमांड कई मैचों को लौटाएगा, न कि केवल एक और उनमें से प्रत्येक मैच को दूसरे द्वारा दोहराया जाएगा find। इसका मतलब यह है कि कुछ बिंदु पर आप उसी फ़ाइल की खुद से तुलना कर रहे होंगे, md5sum समान होगा और आप अपनी सभी फ़ाइलों को हटा देंगे (मैंने इसे परीक्षण dir युक्त पर चलाया ) a.jpgऔर b.jpg:

    for i in $(find . -iname "*.jpg"); do
      for j in $(find . -iname "*.jpg"); do
         echo "i is: $i and j is: $j"
      done
    done   
    i is: ./a.jpg and j is: ./a.jpg   ## BAD, will delete a.jpg
    i is: ./a.jpg and j is: ./b.jpg
    i is: ./b.jpg and j is: ./a.jpg
    i is: ./b.jpg and j is: ./b.jpg   ## BAD will delete b.jpg
    
  • for i in directory_pathजब तक आप निर्देशिकाओं की एक सरणी नहीं दे रहे हैं, तब तक आप चलाना नहीं चाहते हैं। यदि ये सभी फाइलें एक ही डायरेक्टरी में हैं, तो आप for i in $(find directory_path -iname "*.jpg"सभी फाइलों से गुजरना चाहते हैं )।

  • यह है एक बुरा विचार का उपयोग करने के forलिए ढूंढें के उत्पादन के साथ छोरों। आपको whileलूप या ग्लोबिंग का उपयोग करना चाहिए :

    find . -iname "*.jpg" | while read i; do [...] ; done

    या, यदि आपकी सभी फाइलें उसी निर्देशिका में हैं:

    for i in *jpg; do [...]; done

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

  • अंत में, आपको अपने चर को भी उद्धृत करना चाहिए और रिक्त स्थान के साथ निर्देशिका पथ आपकी स्क्रिप्ट को तोड़ देगा।

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

find dir_path -type f -iname "*.jpg" -print0 | while IFS= read -r -d '' i; do
  find dir_path -type f -iname "*.jpg" -print0 | while IFS= read -r -d '' j; do
    if [ "$i" != "$j" ]
    then
      sum1=$(md5sum "$i" | cut -f 1 -d ' ' )
      sum2=$(md5sum "$j" | cut -f 1 -d ' ' )
      [ "$sum1" = "$sum2" ] && rm "$j"
    fi
  done
done

इससे भी सरल तरीका होगा:

find directory_path -name "*.jpg" -exec md5sum '{}' + | 
 perl -ane '$k{$F[0]}++; system("rm $F[1]") if $k{$F[0]}>1'

एक बेहतर संस्करण जो फ़ाइल नामों में रिक्त स्थान से निपट सकता है:

find directory_path -name "*.jpg" -exec md5sum '{}' + | 
 perl -ane '$k{$F[0]}++; system("rm \"@F[1 .. $#F]\"") if $k{$F[0]}>1'

यह थोड़ा पर्ल स्क्रिप्ट findकमांड (यानी md5sum और फ़ाइल नाम) के परिणामों के माध्यम से चलेगा । -aके लिए विकल्प perlखाली स्थान के पर विभाजन इनपुट लाइनों और उन में बचत होती है Fसरणी, इसलिए $F[0]md5sum और हो जाएगा $F[1]फ़ाइल नाम। Md5sum हैश में सहेजा जाता है kऔर स्क्रिप्ट जांचती है कि क्या हैश पहले से ही देखा गया है ( if $k{$F[0]}>1) और फाइल को डिलीट कर देता है यदि यह ( system("rm $F[1]")) है।


जबकि यह काम करेगा, यह बड़े छवि संग्रहों के लिए बहुत धीमा होगा और आप यह नहीं चुन सकते हैं कि कौन सी फाइल रखनी है। कई कार्यक्रम हैं जो इसे और अधिक सुरुचिपूर्ण तरीके से संभालते हैं:


पर्ल स्निपेट के लिए +1। वास्तव में सुरुचिपूर्ण! आप कॉल unlinkकरने के बजाय पर्ल के स्वयं का उपयोग कर सकते हैं system
जोसेफ आर।

@JosephR। धन्यवाद :)। एक बग था, हालांकि, यह रिक्त स्थान के साथ फ़ाइल नामों के लिए विफल हो जाएगा क्योंकि पहले स्थान तक केवल नाम के पहले वर्ण में होगा $F[1]। यह सरणी स्लाइस का उपयोग कर फिक्स्ड। अनलिंक () के रूप में मुझे पता है, लेकिन न्यूनतम को बनाए रखना चाहता था और सिस्टम कॉल को समझना आसान है अगर आप पर्ल को नहीं जानते हैं।
terdon

13

एक निफ्टी प्रोग्राम है, fdupesजो पूरी प्रक्रिया को सरल करता है और उपयोगकर्ता को डुप्लिकेट को हटाने के लिए प्रेरित करता है। मुझे लगता है कि यह जाँच के लायक है:

$ fdupes --delete DIRECTORY_WITH_DUPLICATES
[1] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz        
[2] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz.1

Set 1 of 1, preserve files [1 - 2, all]: 1

   [+] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz
   [-] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz.1

असल में, इसने मुझे किस फाइल को रखने के लिए प्रेरित किया , मैंने 1 टाइप किया , और इसने दूसरा हटा दिया।

अन्य दिलचस्प विकल्प हैं:

-r --recurse
    for every directory given follow subdirectories encountered within

-N --noprompt
    when used together with --delete, preserve the first file in each set of duplicates and delete the others without prompting the user

अपने उदाहरण से, आप शायद इसे इस रूप में चलाना चाहते हैं:

fdupes --recurse --delete --noprompt DIRECTORY_WITH_DUPLICATES

man fdupesउपलब्ध सभी विकल्पों के लिए देखें ।

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