बैश का उपयोग करके प्रत्येक उप-निर्देशिका में एक क्रिया करें


196

मैं एक स्क्रिप्ट पर काम कर रहा हूं, जिसमें एक विशिष्ट फ़ोल्डर की प्रत्येक उप-निर्देशिका में एक क्रिया करने की आवश्यकता होती है।

यह लिखने का सबसे कुशल तरीका क्या है?


1
कृपया सही तरीके से जवाब देने और पुनर्मूल्यांकन के माध्यम से वापस आने पर विचार करें - आपको प्रमुख बग (f / e) के बावजूद बहुत सारे विचार प्राप्त करने के लिए एक स्वीकृत उत्तर मिला है, इसे उस निर्देशिका पर चलाना जहां कोई व्यक्ति पहले भाग गया mkdir 'foo * bar'हो fooऔर barभले ही इससे अधिक पुनरावृत्त हुआ हो वे मौजूद नहीं हैं, और सभी फ़ाइलनामों *की सूची के साथ प्रतिस्थापित किया जाएगा , यहां तक ​​कि गैर-निर्देशिका वाले भी)।
चार्ल्स डफी

1
... और भी बुरा है अगर कोई भागा mkdir -p '/tmp/ /etc/passwd /'- अगर कोई इस प्रथा का पालन करने के /tmpलिए स्क्रिप्ट चलाता है , तो कहना, हटाने के लिए निर्देशिका खोजें, वे समाप्त कर सकते हैं /etc/passwd
चार्ल्स डफी

जवाबों:


178
for D in `find . -type d`
do
    //Do whatever you need with D
done

81
यह सफेद स्थानों पर टूट जाएगा
भूतडॉग Oct४

2
इसके अलावा, ध्यान दें findकि यदि आप पुनरावर्ती या गैर-पुनरावर्ती व्यवहार चाहते हैं, तो आपको पैरामेट्स को मोड़ने की आवश्यकता है ।
क्रिस टोंकिंसन

32
उपर्युक्त उत्तर ने मुझे आत्म निर्देशिका भी दी, इसलिए निम्नलिखित ने मेरे लिए थोड़ा बेहतर काम किया: find . -mindepth 1 -type d
०१५

1
@JoshC, दोनों वेरिएंट mkdir 'directory name with spaces'चार अलग-अलग शब्दों में बनाई गई डायरेक्टरी को तोड़ देंगे ।
चार्ल्स डफी

4
मुझे जोड़ने की जरूरत थी -mindepth 1 -maxdepth 1या यह बहुत गहरा गया।
स्क्रेपीडेव

283

एक संस्करण जो एक उप-प्रक्रिया बनाने से बचता है:

for D in *; do
    if [ -d "${D}" ]; then
        echo "${D}"   # your processing here
    fi
done

या, यदि आपकी कार्रवाई एकल आदेश है, तो यह अधिक संक्षिप्त है:

for D in *; do [ -d "${D}" ] && my_command; done

या इससे भी अधिक संक्षिप्त संस्करण ( धन्यवाद @enzotib )। ध्यान दें कि इस संस्करण में प्रत्येक मान में Dएक अनुगामी स्लैश होगा:

for D in */; do my_command; done

48
आप इसके साथ ifया इससे बच सकते हैं [:for D in */; do
enzotib

2
+1 क्योंकि निर्देशिका के नाम के साथ शुरू नहीं होता है। / जैसा कि स्वीकृत उत्तर के विपरीत है
हेरी उउर कोल्टुक

3
यह एक निर्देशिका नाम 1 में रिक्त स्थान तक भी सही है
एलेक्स रिंकिंग

5
अंतिम कमांड के साथ एक समस्या है: यदि आप उपनिर्देशिका के बिना एक निर्देशिका में हैं; यह "* /" वापस आएगा। तो बेहतर है कि दूसरी कमांड for D in *; do [ -d "${D}" ] && my_command; doneया दो नवीनतम के संयोजन का उपयोग करें :for D in */; do [ -d $D ] && my_command; done
क्रिस मेस

5
ध्यान दें कि यह उत्तर छिपी निर्देशिकाओं की उपेक्षा करता है। इसके for D in .* *; doबजाय छिपे निर्देशिका का उपयोग करने के लिए for D in *; do
patryk.beza

106

सबसे सरल गैर पुनरावर्ती तरीका है:

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

/अंत में बताता है, केवल उपयोग निर्देशिका।

इसकी कोई जरूरत नहीं है

  • खोज
  • awk
  • ...

11
नोट: इसमें डॉट dirs शामिल नहीं होंगे (जो कि एक अच्छी बात हो सकती है, लेकिन यह जानना महत्वपूर्ण है)।
समझदार

ध्यान दें कि shopt -s dotglobवाइल्डकार्ड्स का विस्तार करते समय आप डॉटफाइल्स / डॉटर्ड को शामिल करने के लिए उपयोग कर सकते हैं । इन्हें भी देखें: gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
Steen Schütt

मुझे लगता है कि आप का मतलब /*के बजाय */साथ /पथ का उपयोग करना चाहते का प्रतिनिधित्व।
Br --tsyorfuzthrāx 15

2
@ शूल /*निरपेक्ष पथ के लिए */होगा, जबकि वर्तमान स्थान से उपनिर्देशिकाएं शामिल होंगी
Dan G

3
सहायक संकेत: यदि आपको $ d से ट्रेलिंग '/' ट्रिम करने की आवश्यकता है, तो उपयोग करें${d%/*}
Hafthor

18

उपयोग findकमांड का ।

GNU में find, आप -execdirपैरामीटर का उपयोग कर सकते हैं :

find . -type d -execdir realpath "{}" ';'

या -execपैरामीटर का उपयोग करके :

find . -type d -exec sh -c 'cd -P "$0" && pwd -P' {} \;

या xargsकमांड के साथ :

find . -type d -print0 | xargs -0 -L1 sh -c 'cd "$0" && pwd && echo Do stuff'

या लूप के लिए उपयोग करना :

for d in */; { echo "$d"; }

पुनरावृत्ति के लिए **/इसके बजाय विस्तारित ग्लोबिंग ( ) सक्षम करें (इसके द्वारा सक्षम करें shopt -s extglob)।


अधिक उदाहरणों के लिए, देखें: प्रत्येक निर्देशिका में कैसे जाएं और एक कमांड निष्पादित करें? एसओ पर


1
-exec {} +POSIX- निर्दिष्ट है, -exec sh -c 'owd=$PWD; for arg; do cd -- "$arg" && pwd -P; cd -- "$owd"; done' _ {} +एक और कानूनी विकल्प है, और इससे कम गोले का आह्वान करता है -exec sh -c '...' {} \;
चार्ल्स डफी

13

हैंडी वन-लाइनर्स

for D in *; do echo "$D"; done
for D in *; do find "$D" -type d; done ### Option A

find * -type d ### Option B

विकल्प A, बीच में रिक्त स्थान वाले फ़ोल्डर के लिए सही है। इसके अलावा, आम तौर पर तेजी से क्योंकि यह प्रत्येक शब्द को फ़ोल्डर नाम में एक अलग इकाई के रूप में प्रिंट नहीं करता है।

# Option A
$ time for D in ./big_dir/*; do find "$D" -type d > /dev/null; done
real    0m0.327s
user    0m0.084s
sys     0m0.236s

# Option B
$ time for D in `find ./big_dir/* -type d`; do echo "$D" > /dev/null; done
real    0m0.787s
user    0m0.484s
sys     0m0.308s

10

find . -type d -print0 | xargs -0 -n 1 my_command


क्या मुझे लूप का रिटर्न मूल्य मिल सकता है? यह एक बड़ी स्क्रिप्ट का हिस्सा है ...
mikewilliamson

@ माइक, संभावना नहीं है। $? संभवत: आपको ढूंढने या xargs कमांड की स्थिति प्राप्त होगी, बजाय my_command
पॉल टॉम्बलिन

7

यह एक सबस्क्रिप्शन बनाएगा (जिसका अर्थ है कि whileलूप से बाहर निकलने पर परिवर्तनीय मान खो जाएगा ):

find . -type d | while read -r dir
do
    something
done

यह नहीं होगा:

while read -r dir
do
    something
done < <(find . -type d)

यदि निर्देशिका नाम में रिक्त स्थान हैं तो या तो कोई काम करेगा।


अजीब फ़ाइलनामों के बेहतर हैंडलिंग के लिए (व्हॉट्सएप के साथ समाप्त होने वाले नाम और / या लाइनफ़ीड शामिल हैं), उपयोग करें find ... -print0औरwhile IFS="" read -r -d $'\000' dir
गॉर्डन डेविसन

@GordonDavisson, ... वास्तव में, मैं यह भी तर्क दूंगा कि -d ''बैश सिंटैक्स और क्षमताओं के बारे में कम भ्रामक है, क्योंकि -d $'\000'इसका तात्पर्य $'\000'किसी तरह से अलग है ''- वास्तव में, कोई आसानी से (और फिर, झूठा) से अनुमान लगा सकता है। यह बैश C स्ट्रिंग्स के बजाय पास्कल-स्टाइल स्ट्रिंग्स (लंबाई-निर्दिष्ट, एनयूएल शाब्दिक शामिल करने में सक्षम) का समर्थन करता है (एनयूएल सीमांकित, एनयूएल को शामिल करने में असमर्थ)।
चार्ल्स डफी

5

तुम कोशिश कर सकते हो:

#!/bin/bash
### $1 == the first args to this script
### usage: script.sh /path/to/dir/

for f in `find . -maxdepth 1 -mindepth 1 -type d`; do
  cd "$f"
  <your job here>
done

या इसी के समान...

स्पष्टीकरण:

find . -maxdepth 1 -mindepth 1 -type d: केवल 1 की अधिकतम पुनरावर्ती गहराई (केवल $ 1 की उपनिर्देशिका) और 1 की न्यूनतम गहराई (वर्तमान फ़ोल्डर को छोड़कर .) के साथ निर्देशिकाएं खोजें


यह छोटी गाड़ी है - रिक्त स्थान के साथ एक निर्देशिका नाम के साथ प्रयास करें। बशपिटल्स # 1 देखें , और डॉन्टरेडलाइनसविथफ़ॉर
चार्ल्स डफी

रिक्त स्थान का निर्देशिका नाम उद्धरणों में संलग्न है और इसलिए काम करता है और ओपी फ़ाइल से लाइनों को पढ़ने की कोशिश नहीं कर रहा है।
हेनरी डॉबसन

यह काम करता है cd "$f"। यह तब काम नहीं करता है जब से आउटपुट findस्ट्रिंग-स्प्लिट होता है, इसलिए आपके पास नाम के अलग-अलग टुकड़े होंगे, जिसमें अलग-अलग मान होंगे $f, जिससे आप कितनी अच्छी तरह से $fविस्तार करते हैं या नहीं करते हैं ।
चार्ल्स डफी

मैंने नहीं कहा कि वे एक फ़ाइल से लाइनों को पढ़ने की कोशिश कर रहे थेfindका आउटपुट लाइन-ओरिएंटेड है (एक नाम एक लाइन में, सिद्धांत रूप में - लेकिन नीचे देखें) डिफ़ॉल्ट -printएक्शन के साथ।
चार्ल्स डफी

लाइन-ओरिएंटेड आउटपुट, चूंकि मनमाने ढंग से फाइलनामों को पास करने का find -printएक सुरक्षित तरीका नहीं है , क्योंकि कोई भी कुछ चला सकता है mkdir -p $'foo\n/etc/passwd\nbar'और एक निर्देशिका प्राप्त कर सकता है, जिसके नाम /etc/passwdमें एक अलग लाइन है। देखभाल के बिना /uploadया /tmpनिर्देशिकाओं में फ़ाइलों से नाम संभालना विशेषाधिकार वृद्धि हमलों को प्राप्त करने का एक शानदार तरीका है।
चार्ल्स डफी

4

यदि स्वीकृत नाम उनके पास है, तो स्वीकृत उत्तर श्वेत स्थानों पर टूट जाएगा और पसंदीदा वाक्यविन्यास $()बैश / ksh के लिए है। उदाहरण के GNU find -exec साथ विकल्प का उपयोग करें+;

find .... -exec mycommand +; #this is same as passing to xargs

या थोड़ी देर लूप का उपयोग करें

find .... |  while read -r D
do
  ...
done 

क्या परम निर्देशिका नाम का आयोजन करेगा? उदाहरण के लिए, chmod + x $ DIR_NAME (हाँ, मुझे पता है कि केवल निर्देशिकाओं के लिए एक chmod विकल्प है)
माइक ग्राफ

के बीच find -execऔर गुजरने के बीच एक सूक्ष्म अंतर है xargs: findनिष्पादित किए जा रहे कमांड के निकास मूल्य को अनदेखा करेगा, जबकि xargsएक गैर-अक्षीय निकास पर विफल होगा। या तो सही हो सकता है, आपकी आवश्यकताओं के आधार पर।
जेसन वोडिका

find ... -print0 | while IFS= read -r dसुरक्षित है - व्हॉट्सएप में शुरू या समाप्त होने वाले नामों का समर्थन करता है, और ऐसे नाम जिनमें न्यूलाइन शाब्दिक हैं।
चार्ल्स डफी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.