उनके नाम में \ n वर्ण वाले फ़ोल्डरों में लूप के लिए


9

मेरे पास कुछ फ़ोल्डर हैं जिनमें \nयह उनके नाम हैं।

उदाहरण के लिए:

$ ls
''$'\n''Test'

Thats के नाम के साथ एक फ़ोल्डर का उल्लेख होता है टेस्ट नाम और उसके नाम से पहले एक खाली लाइन।

इसलिए जब मैं इस तरह की कुछ स्क्रिप्ट चलाता हूं, तो इसकी मूल निर्देशिका में:

while IFS= read -r d; do 
    rmdir $d
done < <(find * -type d)

यह दिखाता है:

rmdir: failed to remove '': No such file or directory
rmdir: failed to remove 'Test': No such file or directory

क्योंकि यह दो बार चलता है, एक बार \nऔर दूसरे पर Test, क्योंकि फ़ोल्डर नाम में दो लाइनें हैं।

तो मैं इस मुद्दे को कैसे हल कर सकता हूं कि, स्क्रिप्ट जानता है \nTestकि सिर्फ एक फ़ोल्डर है?


3
आपको खोज के -print0निर्देश, और -dपढ़ने के विकल्प का उपयोग करने की आवश्यकता है । देखें stackoverflow.com/a/40189667/7552
ग्लेन जैकमैन

1
@glennjackman कृपया जवाब दें!
मिठाई

@glennjackman आपके उत्तर के लिए धन्यवाद, लेकिन find * -type d -print0 | while IFS= read -d '' file ; do rmdir $file ; doneकमांड में यह आउटपुट है rmdir: failed to remove 'Test': No such file or directory
तारा एस वोल्प

5
आप चर को अवश्य उद्धृत करें:rmdir "$file"
ग्लेन जैकमैन

1
@glennjackman उत्तर के रूप में इसे पोस्ट करें। यह एक उचित समाधान है और टिप्पणियों के अलावा इसके लिए सबसे अच्छी जगह नहीं है। अपवोट पहले से ही निहित है :)
सर्गी कोलोडाज़नी

जवाबों:


12

आपके पास केवल एक ही आदेश है, इसलिए यह फ्लैग कॉलिंग के findसाथ कॉल करने के लिए पर्याप्त है :-execrmdir

find -depth -type d -exec rmdir {} \;

या के -deleteरूप में विकल्प का उपयोग करें find -type d -delete, लेकिन यह गैर-खाली निर्देशिकाओं के साथ काम नहीं करेगा। उसके लिए आपको -emptyध्वज की भी आवश्यकता होगी । ध्यान दें, -deleteइसका मतलब -depthहै कि छोड़ दिया जा सकता है। इस प्रकार एक और व्यवहार्य विकल्प जो सब कुछ को एक प्रक्रिया के रूप में रखता है:

find -type d -empty -delete

यदि निर्देशिका खाली नहीं है, तो उपयोग करें rm -rf {} \;\nफ़ाइल नाम के साथ केवल निर्देशिकाओं को अलग करने के लिए हम बैश के ANSI-C $'...' को -nameऑप्शन के साथ जोड़ सकते हैं:

find  -type d -name $'*\n*' -empty -delete

POSIX- गीत, हम इसे इस तरह से संभाल सकते हैं:

find -depth  -type d -name "$(printf '*\n*' )" -exec rmdir {} \;

यह ध्यान देने योग्य है कि यदि आपका लक्ष्य निर्देशिकाओं को हटा रहा है, तो -deleteपर्याप्त है, हालांकि यदि आप निर्देशिका पर एक कमांड निष्पादित करना चाहते हैं तो -execसबसे उपयुक्त है।

यह सभी देखें


2
... rm -rf देखभाल के साथ उपयोग करें । ? पी findएक -deleteविकल्प नहीं है btw? यह खाली निर्देशिकाओं को हटाता है और गैर-रिक्त लोगों के लिए त्रुटियों को फेंकता है जैसे rmdir- कम महंगा हो सकता है।
मिठाई

3
@ डर्टर्ट यह करता है और मैंने इसमें जोड़ा है, लेकिन -deleteगैर-रिक्त निर्देशिका को नहीं हटाएगा। इसलिए, सावधान का उपयोग करेंrm -rf
सेर्गेई कोलोडिझनी

6
हमेशा जैसे सूखी रन find(यानी निकाले जाने का अधिकार विनाशकारी पेलोड के बिना आदेशों -deleteया -exec whatever \;अगर प्रभावित फ़ाइलों की सूची वास्तव में सही है और सामान है कि हटा दिया जाता है नहीं करना चाहिए शामिल नहीं है ... जाँच करने के लिए)
बाइट कमांडर

2
यह askubuntu.com है इसलिए हम GNU को मान सकते हैं find-exec rmdir {} +एक rmdirकमांड लाइन पर कई आर्ग्स को बैचने के लिए उपयोग करें । और BTW " लेकिन यह गैर-रिक्त निर्देशिकाओं के साथ काम नहीं करेगा। " rmdirसाथ ही साथ लागू होता है , इसलिए यह वास्तव में इससे भिन्न नहीं है -delete। इससे फर्क पड़ता है rm -r
पीटर कॉर्डेस

2
find -type d -empty -delete( -deleteतात्पर्य -depth)।
केविन

10

आप इसके बजाय शेल ग्लब्स का उपयोग कर सकते हैं find:

for d in */ ; do 
    rmdir "$d"
done

शेल ग्लोब */वर्तमान निर्देशिका में सभी फ़ोल्डरों से मेल खाता है। यह लूप निर्माण के लिए स्वचालित रूप से सही शब्द विभाजन का ध्यान रखता है।

ध्यान दें कि आपके शेल विकल्पों के आधार पर, यह छिपे हुए फ़ोल्डर (नाम के साथ शुरू होने वाला नाम) को अनदेखा कर सकता है। कमांड का उपयोग करके वर्तमान सत्र के लिए सभी फ़ाइलों के मिलान के लिए उस व्यवहार को बदला जा सकता है shopt -s dotglob

इसके अलावा हमेशा अपने चर को उद्धृत करने के लिए मत भूलना।


ग्लोब को केवल वर्तमान निर्देशिका में फ़ाइलें / निर्देशिकाएँ find
मिलेंगी

1
तुम सही हो @ilkkachu। ग्लोबस्टार शेल विकल्प को सक्षम shopt -s globstarकरके और **/*/इसके बजाय ग्लोब का उपयोग करके इसे बदला जा सकता है ।
बाइट कमांडर

6

दोनों उत्तर अब तक rmdirप्रति निर्देशिका में एक बार लिखे गए हैं , लेकिन जैसा rmdirकि मुझे लगता है कि कई तर्क ले सकते हैं: क्या कोई अधिक कुशल तरीका नहीं है?

एक बस कर सकता है

rmdir */

और यह निश्चित रूप से सबसे आसान और सबसे कुशल तरीका है, लेकिन यह कई निर्देशिकाओं के मामले में एक त्रुटि फेंक सकता है (देखें कि गनोम-टर्मिनल में कमांड लाइन तर्कों की अधिकतम लंबाई क्या है? )। यदि आप चाहते हैं कि यह दृष्टिकोण पुनरावर्ती रूप से काम करे, तो इसके बजाय globstarशेल विकल्प को सक्षम shopt -s globstarऔर उपयोग करें ।**/*/*/

GNU के साथ find(और यदि हम उपयोग नहीं करना चाहते हैं -delete), हम कर सकते हैं

find -depth -type d -exec rmdir {} +

जो कमांड लाइन को "उसी तरह xargsबनाता है जो अपनी कमांड लाइनों को बनाता है" ( man find)। स्थानापन्न -depthके लिए -maxdepth 1यदि आप इसे रिकर्सिवली काम करने के लिए नहीं करना चाहती।

एक तीसरा और IMO शानदार तरीका स्टीलराइवर द्वारा इस उत्तर में समझाया गया है :

printf '%s\0' */ | xargs -0 rmdir

यह printfशून्य-सीमांकित तर्क सूची बनाने के लिए शेल बिलिन का उपयोग करता है , इस सूची को फिर से पाइप किया जाता है, xargsजो rmdirबिल्कुल आवश्यक के रूप में कॉल करता है। आप इसे ऊपर के बजाय shopt -s globstarऔर **/*/इसके बजाय पुनरावर्ती */रूप से कार्य कर सकते हैं ।


2
findपुनरावर्ती है, लेकिन ओपी का लूप नहीं है। उस व्यवहार को दोहराने के लिए, का उपयोग करें find -maxdepth 1 -type d -exec rmdir {} +। ( -depthउस मामले में निरर्थक है।) printfअनुकरण के साथ नीट की चाल find -print0, मैंने पहले ऐसा नहीं देखा था।
पीटर कॉर्डेस

1
ओह! मैंने देखा lsऔर whileपाश, मैं याद किया है कि वे के साथ एक प्रक्रिया प्रतिस्थापन से पुनः निर्देशित कर रहे थे findबजाय पाइप lsलूप में और बस किसी कारण से यह दिखाई नहीं दे रहा। जो find *वास्तव में दफन है। हाँ यह पुनरावर्ती है, और यदि निर्देशिका में बहुत सी निर्देशिका प्रविष्टियाँ हैं, तो गैर-निर्देशिकाओं सहित, क्योंकि यह उपयोग नहीं होती है, विफल हो जाएगी */find .ज्यादा बेहतर होगा, क्योंकि बैश के ग्लोब विस्तार की तुलना findमें तेज है stat
पीटर कॉर्ड्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.