प्रत्येक निर्देशिका में कैसे जाएं और एक कमांड निष्पादित करें?


143

मैं एक बश लिपि कैसे लिखूं जो एक पेरेंट_निर्देशक के अंदर प्रत्येक डायरेक्टरी से गुजरती है और प्रत्येक डायरेक्टरी में एक कमांड निष्पादित करती है

निर्देशिका संरचना इस प्रकार है:

parent_directory (नाम कुछ भी हो सकता है - एक पैटर्न का पालन नहीं करते)

  • 001 (निर्देशिका नाम इस पैटर्न का अनुसरण करते हैं)
    • 0001.txt (फ़ाइल नाम इस पैटर्न का अनुसरण करते हैं)
    • 0002.txt
    • 0003.txt
  • 002
    • 0001.txt
    • 0002.txt
    • 0003.txt
    • 0004.txt
  • 003
    • 0001.txt

निर्देशिकाओं की संख्या अज्ञात है।

जवाबों:


108

आप निम्न कार्य कर सकते हैं, जब आपकी वर्तमान निर्देशिका है parent_directory:

for d in [0-9][0-9][0-9]
do
    ( cd "$d" && your-command-here )
done

(और ), एक subshell बनाने इसलिए वर्तमान निर्देशिका मुख्य लिपि में नहीं बदला है।


5
यहां कोई फर्क नहीं पड़ता क्योंकि वाइल्डकार्ड संख्याओं के अलावा किसी और चीज से मेल नहीं खाता है, लेकिन सामान्य स्थिति में, आप मूल रूप से हमेशा निर्देशिका नाम को लूप के अंदर दोहरे उद्धरणों में रखना चाहते हैं। cd "$d"इसमें बेहतर होगा कि यह उन स्थितियों में स्थानांतरित हो जाए जहां वाइल्डकार्ड उन फाइलों से मेल खाता है जिनके नाम में व्हॉट्सएप और / या शेल मेटाचैकर हैं।
ट्रिपलए

1
(यहां सभी उत्तरों में एक ही दोष है, लेकिन यह सबसे ज्यादा वोट देने वाले उत्तर में सबसे ज्यादा मायने रखता है।)
tripleee

1
इसके अलावा, अधिकांश कमांड वास्तव में परवाह नहीं करते हैं कि आप उन्हें किस निर्देशिका में निष्पादित करते हैं। for f in foo bar; do cat "$f"/*.txt; done >outputकार्यात्मक रूप से समतुल्य है cat foo/*.txt bar/*.txt >output। हालाँकि, lsएक आदेश है कि आप इसे कैसे पास करते हैं इसके आधार पर थोड़ा अलग आउटपुट उत्पन्न करता है; यदि आप इसे एक उपनिर्देशिका से चलाते हैं (लेकिन अक्सर, आप उस कारण के लिए ठीक उपनिर्देशिका में जाने से बचना चाहते हैं) तो इसी तरह, grepया wcएक रिश्तेदार फ़ाइल नाम को अलग करता है।
ट्रिपल एपी

158

टॉड द्वारा पोस्ट किए गए इस जवाब ने मेरी मदद की।

find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;

\( ! -name . \) टाल मौजूदा निर्देशिका में कमांड को क्रियान्वित।


4
और अगर आप केवल कुछ फ़ोल्डरों में कमांड चलाना चाहते हैं:find FOLDER* -maxdepth 0 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;
मार्टिन के

3
मुझे करना था -exec bash -c 'cd "$0" && pwd' {} \;क्योंकि कुछ निर्देशिकाओं में एकल उद्धरण और कुछ दोहरे उद्धरण शामिल थे।
चार्ली गोरीचानाज़

12
तुम भी द्वारा जोड़ने वर्तमान निर्देशिका छोड़ सकता है-mindepth 1
mdziob

इसे एक फंक्शन में कैसे बदला जाए, pwdसीधे "git Remote get-url Origin 'जैसी कमांड को स्वीकार करें ?
roachsinai

disd(){find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c 'cd "{}" && "$@"' \;}काम नहीं।
रोशसिनई

48

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

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

या ( @gniourf_gniourf टिप्पणी के अनुसार ):

find . -type d -execdir sh -c 'printf "%s/%s\n" "$PWD" "$0"' {} \;

नोट: आप सामने वाले को ठीक करने के ${0#./}बजाय उपयोग कर सकते हैं ।$0./

या अधिक व्यावहारिक उदाहरण:

find . -name .git -type d -execdir git pull -v ';'

यदि आप वर्तमान निर्देशिका को शामिल करना चाहते हैं, तो यह उपयोग करके और भी सरल है -exec:

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

या उपयोग कर रहा है xargs:

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

या @gniourf_gniourf द्वारा सुझाए गए इसी तरह के उदाहरण :

find . -type d -print0 | while IFS= read -r -d '' file; do
# ...
done

उपरोक्त उदाहरण उनके नाम में रिक्त स्थान के साथ निर्देशिकाओं का समर्थन करते हैं।


या बैश सरणी में असाइन करके:

dirs=($(find . -type d))
for dir in "${dirs[@]}"; do
  cd "$dir"
  echo $PWD
done

.अपने विशिष्ट फ़ोल्डर का नाम बदलें । यदि आपको पुनरावर्ती चलाने की आवश्यकता नहीं है, तो आप इसका उपयोग कर सकते हैं: dirs=(*)इसके बजाय। उपरोक्त उदाहरण नाम में रिक्त स्थान के साथ निर्देशिकाओं का समर्थन नहीं करता है।

तो जैसा कि @gniourf_gniourf ने सुझाव दिया, एक स्पष्ट लूप का उपयोग किए बिना किसी सरणी में खोजने के आउटपुट को डालने का एकमात्र उचित तरीका बैश 4.4 में उपलब्ध होगा:

mapfile -t -d '' dirs < <(find . -type d -print0)

या अनुशंसित तरीका नहीं है (जिसमें पार्सls करना शामिल है ):

ls -d */ | awk '{print $NF}' | xargs -n1 sh -c 'cd $0 && pwd && echo Do stuff'

उपरोक्त उदाहरण वर्तमान dir (जैसा कि ओपी द्वारा अनुरोध किया गया है) को अनदेखा करेगा, लेकिन यह रिक्त स्थान वाले नामों पर टूट जाएगा।

यह सभी देखें:


1
findएक स्पष्ट लूप का उपयोग किए बिना एक सरणी में आउटपुट डालने का एकमात्र उचित तरीका बैश 4.4 में उपलब्ध होगा mapfile -t -d '' dirs < <(find . -type d -print0)। तब तक, आपका एकमात्र विकल्प लूप का उपयोग करना है (या ऑपरेशन को स्थगित करना find)।
१४:०४ पर gniourf_gniourf

1
वास्तव में, आपको वास्तव में dirsसरणी की आवश्यकता नहीं है; तुम findइतना (जैसे) सुरक्षित रूप से उत्पादन पर पाश सकता है find . -type d -print0 | while IFS= read -r -d '' file; do ...; done:।
१४:४४ बजे gniourf_gniourf

@gniourf_gniourf मैं दृश्यमान हूं और हमेशा सरल दिखने वाली चीजों को खोजने / बनाने की कोशिश कर रहा हूं। उदाहरण के लिए, मैंने यह स्क्रिप्ट बनाई है और बैश एरे का उपयोग करके मेरे लिए आसान और पठनीय है (पाइप का उपयोग करने के बजाय तर्क को छोटे टुकड़ों में अलग करके)। अतिरिक्त पाइप का परिचय, IFS, read, यह अतिरिक्त जटिलता के प्रभाव देता है। मुझे इसके बारे में सोचना होगा, शायद इसे करने का कुछ आसान तरीका है।
kenorb

अगर आसान से आपका मतलब टूटा हुआ है तो हां, करने के आसान तरीके हैं। अब चिंता मत करो, ये सब IFSऔर read(जैसा कि आप कहते हैं) एक दूसरी प्रकृति बन जाते हैं जैसा कि आपको उनकी आदत है ... वे स्क्रिप्ट लिखने के विहित और मुहावरेदार तरीकों का हिस्सा हैं।
gniourf_gniourf

वैसे, आपका पहला कमांड find . -type d -execdir echo $(pwd)/{} ';'वह नहीं करता है जो आप चाहते हैं ( $(pwd)विस्तार होने findसे पहले ही निष्पादित हो जाता है) ...
gniourf_gniourf

29

आप इसे पाइपिंग और फिर उपयोग करके प्राप्त कर सकते हैं xargs। पकड़ आपको -Iध्वज का उपयोग करने की आवश्यकता है जो आपके बैश कमांड में सबरिंग द्वारा प्रतिस्थापित किए गए सबस्ट्रिंग को प्रतिस्थापित करेगा xargs

ls -d */ | xargs -I {} bash -c "cd '{}' && pwd"

आप pwdप्रत्येक निर्देशिका में जो भी कमांड निष्पादित करना चाहते हैं, उसके साथ बदलना चाह सकते हैं ।


2
मुझे नहीं पता कि यह क्यों नहीं अपग्रेड किया गया है, लेकिन सबसे सरल स्क्रिप्ट मुझे अब तक मिली है। धन्यवाद
Linvi

20

यदि toplevel फ़ोल्डर ज्ञात है, तो आप कुछ इस तरह लिख सकते हैं:

for dir in `ls $YOUR_TOP_LEVEL_FOLDER`;
do
    for subdir in `ls $YOUR_TOP_LEVEL_FOLDER/$dir`;
    do
      $(PLAY AS MUCH AS YOU WANT);
    done
done

$ पर (प्ले के रूप में आप चाहते हैं के रूप में खेलते हैं); आप जितना चाहें उतना कोड डाल सकते हैं।

ध्यान दें कि मैंने किसी भी निर्देशिका पर "सीडी" नहीं किया है।

चीयर्स,


3
वहाँ के उदाहरण के एक जोड़े हैं "की अनुपयोगी उपयोग ls" यहाँ - तुम सिर्फ कर सकता है for dir in $YOUR_TOP_LEVEL_FOLDER/*बजाय।
मार्क लोंगेयर

1
ठीक है, हो सकता है कि यह एक सामान्य अर्थ में बेकार हो, लेकिन यह सीधे एलएस में फ़िल्टरिंग करने की अनुमति देता है (यानी सभी निर्देशिका .tmp के साथ समाप्त हो जाती है)। इसलिए मैंने ls $dirअभिव्यक्ति का उपयोग किया
gforcada

13
for dir in PARENT/*
do
  test -d "$dir" || continue
  # Do something with $dir...
done

और यह समझना बहुत आसान है!
रबज

6

जबकि एक लाइनर त्वरित और गंदे उपयोग के लिए अच्छा है, मैं स्क्रिप्ट लिखने के लिए अधिक वर्बोज़ संस्करण के नीचे पसंद करता हूं। यह वह टेम्पलेट है जिसका मैं उपयोग करता हूं जो कई किनारे मामलों की देखभाल करता है और आपको एक फ़ोल्डर पर निष्पादित करने के लिए अधिक जटिल कोड लिखने की अनुमति देता है। आप अपने bash कोड को फंक्शन dir_command में लिख सकते हैं। नीचे, dir_coomand एक उदाहरण के रूप में प्रत्येक रिपॉजिटरी को टैग करते हुए लागू करता है। बाकी स्क्रिप्ट निर्देशिका में प्रत्येक फ़ोल्डर के लिए dir_command को कॉल करती है। फ़ोल्डर के केवल दिए गए सेट के माध्यम से पुनरावृति का उदाहरण भी शामिल है।

#!/bin/bash

#Use set -x if you want to echo each command while getting executed
#set -x

#Save current directory so we can restore it later
cur=$PWD
#Save command line arguments so functions can access it
args=("$@")

#Put your code in this function
#To access command line arguments use syntax ${args[1]} etc
function dir_command {
    #This example command implements doing git status for folder
    cd $1
    echo "$(tput setaf 2)$1$(tput sgr 0)"
    git tag -a ${args[0]} -m "${args[1]}"
    git push --tags
    cd ..
}

#This loop will go to each immediate child and execute dir_command
find . -maxdepth 1 -type d \( ! -name . \) | while read dir; do
   dir_command "$dir/"
done

#This example loop only loops through give set of folders    
declare -a dirs=("dir1" "dir2" "dir3")
for dir in "${dirs[@]}"; do
    dir_command "$dir/"
done

#Restore the folder
cd "$cur"

5

मैं फ़ाइल बनाने के साथ बात नहीं करता, क्योंकि आप केवल फ़ोल्डर्स के माध्यम से पुनरावृति करना चाहते हैं ... क्या आप कुछ इस तरह की तलाश कर रहे हैं?

cd parent
find . -type d | while read d; do
   ls $d/
done

4

आप उपयोग कर सकते हैं

find .

वर्तमान निर्देशिका में सभी फाइलों / डायरियों की खोज करने के लिए

आप आउटपुट को xargs कमांड की तरह पाइप कर सकते हैं

find . | xargs 'command here'

3
यह वह नहीं है जो पूछा गया था।
kenorb

0
for p in [0-9][0-9][0-9];do
    (
        cd $p
        for f in [0-9][0-9][0-9][0-9]*.txt;do
            ls $f; # Your operands
        done
    )
done

आपको निर्देशिका को फिर से वापस बदलने की जरूरत होगी, या cdएक उपधारा में करना होगा ।
मार्क लोंगेयर

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