कुछ एक्सटेंशन के साथ फ़ाइलों को हटाने के लिए एक निर्देशिका के माध्यम से लूप कैसे करें


157

मैं पाश करने के लिए एक निर्देशिका के माध्यम से रिकर्सिवली की जरूरत है और विस्तार के साथ सभी फ़ाइलों को हटा दें .pdfऔर .doc। मैं एक निर्देशिका के माध्यम से पुनरावर्ती रूप से लूप का प्रबंधन कर रहा हूं लेकिन उपरोक्त फ़ाइल एक्सटेंशन के साथ फ़ाइलों को फ़िल्टर करने का प्रबंधन नहीं कर रहा हूं।

मेरा कोड अब तक

#/bin/sh

SEARCH_FOLDER="/tmp/*"

for f in $SEARCH_FOLDER
do
    if [ -d "$f" ]
    then
        for ff in $f/*
        do      
            echo "Processing $ff"
        done
    else
        echo "Processing file $f"
    fi
done

मुझे कोड पूरा करने के लिए सहायता की आवश्यकता है, क्योंकि मैं कहीं भी नहीं मिल रहा हूं।


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

इसी तरह का प्रश्न यहां: stackoverflow.com/questions/41799938/…
कोडफॉस्टर

1
@ कीथ के पास भी ऐसा ही अनुभव था, पूरी तरह से सहमत थे और शीर्षक बदल दिया था
आइडल्ट 463035818

जवाबों:


146

find बस उसी के लिए बना है।

find /tmp -name '*.pdf' -or -name '*.doc' | xargs rm

19
या -deleteविकल्प खोजें।
मैथ्यू फ्लेशेन

28
हमेशा सदुपयोग करना चाहिए find ... -print0 | xargs -0 ..., कच्ची खोज नहीं | नए नाम वाले फ़ाइल नाम के साथ समस्याओं से बचने के लिए xargs।
ग्रंबेल

7
xargsबिना किसी विकल्प के उपयोग करना लगभग हमेशा बुरी सलाह है और यह कोई अपवाद नहीं है। find … -execइसके बजाय उपयोग करें ।
गिल्स एसओ- बुराई को रोकना '

211

मॉविसीएल के जवाब के फॉलोअप के रूप में, आप xargs का उपयोग करने के बजाय लूप के लिए भी ऐसा कर सकते हैं। मुझे अक्सर xargs बोझिल लगता है, खासकर अगर मुझे प्रत्येक पुनरावृत्ति में कुछ अधिक जटिल करने की आवश्यकता होती है।

for f in $(find /tmp -name '*.pdf' -or -name '*.doc'); do rm $f; done

जैसा कि कई लोगों ने टिप्पणी की है, यह तब विफल हो जाएगा जब फ़ाइलनामों में रिक्त स्थान हैं। आप अस्थायी रूप से IFS (आंतरिक फ़ील्ड सेपरेटर) को नए वर्ण पर सेट करके इसके चारों ओर काम कर सकते हैं। यह भी विफल रहता है अगर \[?*फ़ाइल नाम में वाइल्डकार्ड वर्ण हैं । आप वाइल्डकार्ड विस्तार (ग्लोबिंग) को अस्थायी रूप से अक्षम करके उसके चारों ओर काम कर सकते हैं।

IFS=$'\n'; set -f
for f in $(find /tmp -name '*.pdf' -or -name '*.doc'); do rm "$f"; done
unset IFS; set +f

यदि आपके फ़ाइलनाम में नई-नई पंक्तियाँ हैं, तो वह भी काम नहीं करेगी। आप xargs आधारित समाधान के साथ बेहतर हैं:

find /tmp \( -name '*.pdf' -or -name '*.doc' \) -print0 | xargs -0 rm

( -print0दोनों orखंडों में लागू होने के लिए बची हुई कोष्ठक की आवश्यकता होती है ।)

जीएनयू और * बीएसडी में भी एक -deleteकार्रवाई होती है, जो इस तरह दिखाई देगी:

find /tmp \( -name '*.pdf' -or -name '*.doc' \) -delete

27
यह अपेक्षा के अनुरूप काम नहीं करता है यदि फ़ाइल नाम में एक स्थान है (लूप के लिए व्हॉट्सएप पर खोज के परिणामों को विभाजित करता है)।
trev

3
आप व्हाट्सएप पर विभाजन को कैसे समाप्त करते हैं? मैं इसी तरह की कोशिश कर रहा हूं और मेरे पास व्हॉट्सएप के साथ बहुत सारी निर्देशिकाएं हैं जो इस लूप को पेंच करती हैं।
क्रिश्चियन

3
क्योंकि यह एक बहुत ही उपयोगी उत्तर है?
zenperttu

1
@ क्रिसियन इस तरह से उद्धरणों का उपयोग करके व्हॉट्सएप विभाजन को ठीक करें: "$ (खोजें ...)"। मैंने दिखाने के लिए जेम्स का जवाब संपादित किया है।
मैथ्यू

2
@ मैथ्यू ने आपके संपादन को कुछ भी ठीक नहीं किया: यह वास्तव में कमांड केवल तभी काम करता है जब कोई विशिष्ट फ़ाइल मिली हो । फ़ाइलनाम में स्थान, टैब आदि न होने पर कम से कम यह संस्करण काम करता है । मैं पुराने संस्करण में वापस आ गया। समझदार नहीं वास्तव में एक ठीक कर सकते हैं for f in $(find ...)बस इस विधि का उपयोग न करें।
गनीउरफ_निगियॉर्फ

67

इसके बिना find:

for f in /tmp/* tmp/**/* ; do
  ...
done;

/tmp/*dir /tmp/**/*में फ़ाइलें हैं और सबफ़ोल्डर्स में फ़ाइलें हैं। यह संभव है कि आपको ग्लोबस्टार विकल्प ( shopt -s globstar) को सक्षम करना होगा । तो सवाल के लिए कोड इस तरह दिखना चाहिए:

shopt -s globstar
for f in /tmp/*.pdf /tmp/*.doc tmp/**/*.pdf tmp/**/*.doc ; do
  rm "$f"
done

ध्यान दें कि इसके लिए bash (4.0 की आवश्यकता है (या बिना zsh shopt -s globstar, या ksh के set -o globstarबजाय shopt -s globstar)। इसके अलावा, bash <4.3 में, यह ट्रैवर्स को प्रतीकात्मक के साथ-साथ निर्देशिकाओं से जोड़ता है, जो आमतौर पर वांछनीय नहीं है।


1
इस विधि ने मेरे लिए भी काम किया, यहां तक ​​कि OSX पर विचार रखने वाले फ़ाइलनामों के साथ
विचारशील

2
वर्थ नोटिंग कि ग्लोबस्टार केवल बैश 4.0 या नए में उपलब्ध है .. जो कई मशीनों पर डिफ़ॉल्ट संस्करण नहीं है।
ट्रॉय हॉवर्ड

1
मुझे नहीं लगता कि आपको पहले तर्क को निर्दिष्ट करने की आवश्यकता है। (कम से कम आज के रूप में,) for f in /tmp/**पर्याप्त होगा। / Tmp dir से फ़ाइलें शामिल हैं।
दर्शन २

1
यह इस तरह बेहतर नहीं होगा? for f in /tmp/*.{pdf,doc} tmp/**/*.{,pdf,doc} ; do
आइस-ब्लेज़

1
**एक अच्छा विस्तार है, लेकिन POSIX के लिए पोर्टेबल नहीं है sh। (यह सवाल बैश को टैग किया गया है, लेकिन यह बताना अच्छा होगा कि यहां कई समाधानों के विपरीत, यह वास्तव में हैश-ओनली है। या, ठीक है, यह कई अन्य विस्तारित गोले में भी काम करता है।)
tripleee

27

यदि आप पुनरावर्ती रूप से कुछ करना चाहते हैं, तो मेरा सुझाव है कि आप पुनरावर्तन का उपयोग करें (हाँ, आप इसे ढेर आदि का उपयोग करके कर सकते हैं, लेकिन हे)।

recursiverm() {
  for d in *; do
    if [ -d "$d" ]; then
      (cd -- "$d" && recursiverm)
    fi
    rm -f *.pdf
    rm -f *.doc
  done
}

(cd /tmp; recursiverm)

यह कहा, findशायद एक बेहतर विकल्प है जैसा कि पहले ही सुझाया गया है।


15

यहां शेल ( bash) का उपयोग करके एक उदाहरण दिया गया है :

#!/bin/bash

# loop & print a folder recusively,
print_folder_recurse() {
    for i in "$1"/*;do
        if [ -d "$i" ];then
            echo "dir: $i"
            print_folder_recurse "$i"
        elif [ -f "$i" ]; then
            echo "file: $i"
        fi
    done
}


# try get path from param
path=""
if [ -d "$1" ]; then
    path=$1;
else
    path="/tmp"
fi

echo "base path: $path"
print_folder_recurse $path

15

यह सीधे आपके प्रश्न का उत्तर नहीं देता है, लेकिन आप अपनी समस्या को एक-लाइनर के साथ हल कर सकते हैं:

find /tmp \( -name "*.pdf" -o -name "*.doc" \) -type f -exec rm {} +

खोज के कुछ संस्करणों (GNU, BSD) में एक -deleteक्रिया है जिसे आप कॉल करने के बजाय उपयोग कर सकते हैं rm:

find /tmp \( -name "*.pdf" -o -name "*.doc" \) -type f -delete

7

यह विधि रिक्त स्थान को अच्छी तरह से संभालती है।

files="$(find -L "$dir" -type f)"
echo "Count: $(echo -n "$files" | wc -l)"
echo "$files" | while read file; do
  echo "$file"
done

संपादित करें, एक-एक करके ठीक करता है

function count() {
    files="$(find -L "$1" -type f)";
    if [[ "$files" == "" ]]; then
        echo "No files";
        return 0;
    fi
    file_count=$(echo "$files" | wc -l)
    echo "Count: $file_count"
    echo "$files" | while read file; do
        echo "$file"
    done
}

मुझे लगता है कि गूंज के बाद "-n" ध्वज की आवश्यकता नहीं है। बस इसे स्वयं का परीक्षण करें: "-n" के साथ आपकी स्क्रिप्ट गलत संख्या में फाइलें देती है। निर्देशिका में बिल्कुल एक फ़ाइल के लिए यह "काउंट: 0"
लोपा

1
यह सभी फ़ाइल नामों के साथ काम नहीं करता है: यह नाम के अंत में रिक्त स्थान के साथ विफल रहता है, फ़ाइल नाम के साथ newlines और कुछ फ़ाइल नामों के साथ बैकस्लैश होता है। इन दोषों को ठीक किया जा सकता है लेकिन संपूर्ण दृष्टिकोण अनावश्यक रूप से जटिल है इसलिए यह परेशान करने लायक नहीं है।
गिल्स एसओ- बुराई को रोकना '

3

बैश के लिए (संस्करण 4.0 से):

shopt -s globstar nullglob dotglob
echo **/*".ext"

बस इतना ही।
उस एक्सटेंशन के साथ फाइल (या डायर) चुनने के लिए अनुगामी विस्तार ".ext" है।

विकल्प ग्लोबस्टार ** सक्रिय करता है (खोज पुनरावर्ती)।
ऑप्शन nullglob एक * निकालता है जब यह बिना फाइल / डीआईआर से मेल खाता है।
विकल्प डॉटग्लोब में वे फाइलें शामिल हैं जो एक डॉट (छिपी हुई फाइलें) को शुरू करती हैं।

खबरदार कि 4.3 से पहले, **/यह भी निर्देशिकाओं के प्रतीकात्मक लिंक का पता लगाता है जो वांछनीय नहीं है।


1

निम्नलिखित कार्य \home\ubuntuनिर्देशिका (ubuntu के तहत पूरी निर्देशिका संरचना) में सभी निर्देशिकाओं के माध्यम से पुनरावृत्ति करेगा और elseब्लॉक में आवश्यक जांच लागू करेगा ।

function check {
        for file in $1/*      
        do
        if [ -d "$file" ]
        then
                check $file                          
        else
               ##check for the file
               if [ $(head -c 4 "$file") = "%PDF" ]; then
                         rm -r $file
               fi
        fi
        done     
}
domain=/home/ubuntu
check $domain

1

यह ऐसा करने का सबसे सरल तरीका है: rm **/@(*.doc|*.pdf)

** इस काम को पुनरावर्ती बनाता है

@(*.doc|*.pdf) पीडीएफ या डॉक में समाप्त होने वाली फ़ाइल की तलाश करता है

के rmसाथ बदलकर सुरक्षित रूप से परीक्षण करने के लिए आसान हैls


0

findअन्य उपयोगिता में आउटपुट को पाइप करने का कोई कारण नहीं है । findमें एक -deleteझंडा बनाया गया है।

find /tmp -name '*.pdf' -or -name '*.doc' -delete

0

प्रदान किए गए अन्य उत्तरों में ऐसी फाइलें या निर्देशिकाएं शामिल नहीं होंगी जो एक के साथ शुरू होती हैं। निम्नलिखित मेरे लिए काम किया:

#/bin/sh
getAll()
{
  local fl1="$1"/*;
  local fl2="$1"/.[!.]*; 
  local fl3="$1"/..?*;
  for inpath in "$1"/* "$1"/.[!.]* "$1"/..?*; do
    if [ "$inpath" != "$fl1" -a "$inpath" != "$fl2" -a "$inpath" != "$fl3" ]; then 
      stat --printf="%F\0%n\0\n" -- "$inpath";
      if [ -d "$inpath" ]; then
        getAll "$inpath"
      #elif [ -f $inpath ]; then
      fi;
    fi;
  done;
}

-1

बस करो

find . -name '*.pdf'|xargs rm

4
नहीं, यह मत करो। यह टूट जाता है यदि आपके पास रिक्त स्थान या अन्य अजीब प्रतीकों के साथ फ़ाइल नाम है।
गनीउरफ_ग्निउरफ

-1

निम्नलिखित दिए गए डायरेक्टरी के माध्यम से निम्नलिखित को लूप करेगा और सभी सामग्रियों को सूचीबद्ध करेगा:

for d in /home/ubuntu/*; do echo "listing contents of dir: $d"; ls -l $d/; done


नहीं, यह फ़ंक्शन पुनरावर्ती रूप से किसी भी चीज़ को पार नहीं करता है। यह केवल उपनिर्देशिकाओं की सामग्री को सूचीबद्ध करता है। यह बस के आसपास है ls -l /home/ubuntu/*/, तो यह बहुत बेकार है।
गिल्स एसओ- बुराई को रोकें '

-1

यदि आप कमांड को चलाने के लिए उपयोग किए गए शेल को बदल सकते हैं, तो आप ZSH का उपयोग नौकरी करने के लिए कर सकते हैं।

#!/usr/bin/zsh

for file in /tmp/**/*
do
    echo $file
done

यह सभी फ़ाइलों / फ़ोल्डरों के माध्यम से पुनरावृत्ति करेगा।

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