लिनक्स में निर्देशिकाओं पर लूप कैसे करें?


168

मैं लिनक्स पर बैश में एक स्क्रिप्ट लिख रहा हूं और किसी दिए गए निर्देशिका में सभी उपनिर्देशिका नामों से गुजरने की आवश्यकता है। मैं इन निर्देशिकाओं (और नियमित फ़ाइलों को छोड़) के माध्यम से कैसे लूप कर सकता हूं?

उदाहरण के लिए:
दी गई निर्देशिका /tmp/
यह है कि निम्नलिखित उपनिर्देशिकाएँ हैं:/tmp/A, /tmp/B, /tmp/C

मैं ए, बी, सी को पुनः प्राप्त करना चाहता हूं।


जवाबों:


129
cd /tmp
find . -maxdepth 1 -mindepth 1 -type d -printf '%f\n'

एक संक्षिप्त विवरण:

  • find फ़ाइलें पाता है (काफी स्पष्ट रूप से)

  • .वर्तमान निर्देशिका है, जो cdहै /tmp(IMHO के बाद यह /tmpसीधे findकमांड में होने की तुलना में अधिक लचीला है । आपके पास केवल एक ही स्थान है, cdबदलने के लिए, यदि आप इस फ़ोल्डर में जगह लेना चाहते हैं)

  • -maxdepth 1और -mindepth 1सुनिश्चित करें कि findकेवल वर्तमान निर्देशिका में दिखता है और .परिणाम में खुद को शामिल नहीं करता है

  • -type d केवल निर्देशिका के लिए दिखता है

  • -printf '%f\n प्रत्येक हिट के लिए केवल पाया गया फ़ोल्डर का नाम (साथ ही एक नई रेखा) प्रिंट करता है।

Et voilà!


वैसे: ध्यान दें, कि सभी उत्तरों में छिपे हुए फ़ोल्डर मिलेंगे, वह भी (यानी, डॉट के साथ शुरू होने वाले फ़ोल्डर)! यदि आप ऐसा नहीं चाहते हैं, तो प्रिंटफ या निष्पादित विकल्पों से पहले `-regex '\ / / [^ \]। *' जोड़ें।
बोल्डवैन

1
मददगार - अगर आपको प्रत्येक हिट के लिए केवल एक कमांड निष्पादित करने की आवश्यकता है ;-) - केवल मेरे पास निष्पादित करने के लिए कई कमांड हैं :-(।
मार्टिन

कोई समस्या नहीं: इस समाधान को अनुकूलित करें: transnum.blogspot.de/2008/11/…while..done लूप के अंदर आप पागल हो सकते हैं।
Boldewyn

1
यह कुछ उपयोग के मामलों के लिए एक बहुत अच्छी विधि है; findका -execविकल्प आपको प्रत्येक फ़ाइल / निर्देशिका के लिए कोई भी कमांड चलाने देता है।
jtpereyda

451

सभी जवाब अब तक का उपयोग करते हैं find, इसलिए यहां केवल शेल के साथ एक है। आपके मामले में बाहरी उपकरणों की आवश्यकता नहीं है:

for dir in /tmp/*/     # list directories in the form "/tmp/dirname/"
do
    dir=${dir%*/}      # remove the trailing "/"
    echo ${dir##*/}    # print everything after the final "/"
done

18
+1 - findजब आप वाइल्डकार्ड से स्लैश जोड़ सकते हैं तो परेशान क्यों होते हैं
टोबीस किन्ज़लर

19
तो for dir in */; do echo $dir; doneवर्तमान निर्देशिका में निर्देशिकाओं के लिए है।
एहतेश चौधरी

41
अच्छा होगा यदि इसमें स्पष्टीकरण हो सकता है कि क्या कर रहा है dir=${dir%*/}और क्या echo ${dir##*/}कर रहा है।
जेरेमी एस।

11
यह इसलिए है क्योंकि चर dir में बैकस्लैश शामिल होगा। वह सिर्फ साफ नाम रखने के लिए इसे हटा रहा है। % अंत में / को हटा देगा। ## शुरुआत में / को हटा देगा। ये सब्सट्रिंग को संभालने के लिए ऑपरेटर होते हैं।
1

8
यदि कोई मेल नहीं है, तो लूप को ट्रिगर किया जाएगा /tmp/*/, यह देखने के लिए एक चेक शामिल करना बुद्धिमान होगा कि क्या निर्देशिका वास्तव में मौजूद है।
23:42 पर Jrs42

41

आप सभी निर्देशिकाओं के माध्यम से छिपा सकते हैं जिसमें छिपे हुए निर्देश (डॉट के साथ शुरुआत) शामिल हैं:

for file in */ .*/ ; do echo "$file is a directory"; done

नोट: सूची का उपयोग करना */ .*/zsh में तभी काम करता है जब फ़ोल्डर में कम से कम एक छिपी निर्देशिका मौजूद हो। बैश में यह भी दिखाएगा .और..


छिपा निर्देशिका को शामिल करने के लिए बैश के लिए एक और संभावना का उपयोग करना होगा:

shopt -s dotglob;
for file in */ ; do echo "$file is a directory"; done

यदि आप सिमिलिंक को बाहर करना चाहते हैं:

for file in */ ; do 
  if [[ -d "$file" && ! -L "$file" ]]; then
    echo "$file is a directory"; 
  fi; 
done

प्रत्येक समाधान में केवल अनुगामी निर्देशिका नाम (ए, बी, सी) का उत्पादन करने के लिए छोरों के भीतर इसका उपयोग करें:

file="${file%/}"     # strip trailing slash
file="${file##*/}"   # strip path and leading slash
echo "$file is the directoryname without slashes"

उदाहरण (यह निर्देशिकाओं के साथ भी काम करता है जिसमें रिक्त स्थान होते हैं):

mkdir /tmp/A /tmp/B /tmp/C "/tmp/ dir with spaces"
for file in /tmp/*/ ; do file="${file%/}"; echo "${file##*/}"; done

16

निर्देशिकाओं के साथ काम करता है जिसमें रिक्त स्थान होते हैं

सोरपिगल से प्रेरित है

while IFS= read -d $'\0' -r file ; do 
    echo $file; ls $file ; 
done < <(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d -print0)

मूल पोस्ट (रिक्त स्थान के साथ काम नहीं करता है)

से प्रेरित होकर Boldewyn : के साथ लूप का उदाहरण findआदेश।

for D in $(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d) ; do
    echo $D ;
done


6

मैं जिस तकनीक का सबसे अधिक उपयोग करता हूं वह है find | xargs। उदाहरण के लिए, यदि आप इस निर्देशिका की प्रत्येक फ़ाइल और इसके सभी उपनिर्देशिकाओं को विश्व-पठनीय बनाना चाहते हैं, तो आप ऐसा कर सकते हैं:

find . -type f -print0 | xargs -0 chmod go+r
find . -type d -print0 | xargs -0 chmod go+rx

-print0विकल्प के लिए एक अंतरिक्ष के बजाय एक शून्य चरित्र के साथ समाप्त हो जाता है। -0विकल्प अपने इनपुट उसी तरह बांट देता है। तो यह रिक्त स्थान के साथ फ़ाइलों पर उपयोग करने के लिए संयोजन है।

आप कमांड की इस श्रृंखला को हर लाइन आउटपुट के रूप में ले सकते हैं findऔर इसे chmodकमांड के अंत में चिपका सकते हैं ।

यदि आप जिस कमांड को अंत के बजाय बीच में इसके तर्क के रूप में चलाना चाहते हैं, तो आपको थोड़ा रचनात्मक होना होगा। उदाहरण के लिए, मुझे हर उपनिर्देशिका में बदलने और कमांड चलाने की आवश्यकता थी latemk -c। इसलिए मैंने ( विकिपीडिया से ) प्रयोग किया :

find . -type d -depth 1 -print0 | \
    xargs -0 sh -c 'for dir; do pushd "$dir" && latexmk -c && popd; done' fnord

इसका प्रभाव पड़ता है for dir $(subdirs); do stuff; done, लेकिन उनके नाम में रिक्त स्थान वाली निर्देशिकाओं के लिए सुरक्षित है। इसके अलावा, अलग-अलग कॉल stuffएक ही शेल में किए जाते हैं, यही वजह है कि मेरी कमांड में हमें वर्तमान निर्देशिका के साथ वापस लौटना होगा popd


3

एक न्यूनतम बैश लूप जिसे आप बंद कर सकते हैं (घोस्टडोग्ग74 उत्तर से दूर)

for dir in directory/*                                                     
do                                                                                 
  echo ${dir}                                                                  
done

निर्देशिका द्वारा फ़ाइलों की एक पूरी गुच्छा ज़िप करने के लिए

for dir in directory/*
do
  zip -r ${dir##*/} ${dir}
done                                               

यह directoryउपनिर्देशिका ही नहीं, की सभी फाइलों को सूचीबद्ध करता है ।
डेक्सटेरिटास

2

find . -type d -maxdepth 1


2
यह उत्तर अच्छा है, सिवाय इसके कि मुझे पूरे रास्ते मिलते हैं, जबकि मुझे केवल निर्देशिकाओं के नाम चाहिए।
एरिक सपिर

2

आप पाश के लिए एक में एक से अधिक आदेश पर अमल करना चाहते हैं, तो आप का परिणाम बचा सकता है findके साथ mapfile(बैश> = 4) एक चर के रूप में और के साथ सरणी के माध्यम से जाना ${dirlist[@]}। यह रिक्त स्थान वाली निर्देशिकाओं के साथ भी काम करता है।

findआदेश पर आधारित है इस सवाल का जवाब Boldewyn द्वारा। findकमांड के बारे में और जानकारी वहां पाई जा सकती है।

IFS=""
mapfile -t dirlist < <( find . -maxdepth 1 -mindepth 1 -type d -printf '%f\n' )
for dir in ${dirlist[@]}; do
    echo ">${dir}<"
    # more commands can go here ...
done
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.