วิธีการวนซ้ำไดเรกทอรีใน Linux?


168

ฉันกำลังเขียนสคริปต์ใน bash บน Linux และต้องผ่านชื่อไดเรกทอรีย่อยทั้งหมดในไดเรกทอรีที่กำหนด ฉันจะวนซ้ำไดเรกทอรีเหล่านี้ (และข้ามไฟล์ปกติ) ได้อย่างไร

ตัวอย่างเช่น:
ไดเรกทอรีที่กำหนดคือ/tmp/
มันมีไดเรกทอรีย่อยดังต่อไปนี้:/tmp/A, /tmp/B, /tmp/C

ฉันต้องการดึง A, B, 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 พิมพ์เฉพาะชื่อโฟลเดอร์ที่พบ (รวมถึงบรรทัดใหม่) สำหรับแต่ละ Hit

และอื่น ๆ !


โดยวิธีการ: โปรดทราบว่าคำตอบทั้งหมดจะพบโฟลเดอร์ที่ซ่อนอยู่เช่นกัน (นั่นคือโฟลเดอร์ที่ขึ้นต้นด้วยจุด)! หากคุณไม่ต้องการสิ่งนี้ให้เพิ่ม `-regex '\ ./ [^ \.]. *' 'ก่อนตัวเลือก printf หรือ exec
Boldewyn

1
มีประโยชน์ - หากคุณต้องการดำเนินการเพียงคำสั่งเดียวสำหรับการเข้าชมแต่ละครั้ง ;-) - มีเพียงฉันเท่านั้นที่มีคำสั่งให้เรียกใช้งาน :-(
Martin

ไม่มีปัญหา: ปรับแก้ปัญหานี้: transnum.blogspot.de/2008/11/…ภายในwhile..doneวงคุณสามารถไปบ้า
Boldewyn

1
นี่เป็นวิธีที่ดีมากสำหรับบางกรณีการใช้งาน find's -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เมื่อคุณสามารถเพิ่มเครื่องหมายทับลงในอักขระตัวแทนได้
Tobias Kienzler

19
ดังนั้นfor dir in */; do echo $dir; doneสำหรับไดเรกทอรีในไดเรกทอรีปัจจุบัน
Ehtesh Choudhury

41
คงจะดีถ้ามันจะมีคำอธิบายสิ่งที่dir=${dir%*/}และecho ${dir##*/}จะทำ
Jeremy S.

11
เป็นเพราะตัวแปร dir จะรวมเครื่องหมายแบ็กสแลชที่ท้าย เขาเพิ่งลบออกเพื่อให้มีชื่อที่สะอาด % จะลบ / ในตอนท้าย ## จะลบ / ที่จุดเริ่มต้น นี่คือโอเปอเรเตอร์เพื่อจัดการสตริงย่อย
sfratini

8
หากไม่มีการจับคู่การวนซ้ำจะถูกเรียกใช้ด้วย/tmp/*/ก็ควรที่จะรวมการตรวจสอบเพื่อดูว่ามีไดเรกทอรีอยู่จริงหรือไม่
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

หากต้องการส่งออกเฉพาะชื่อไดเรกทอรีต่อท้าย (A, B, C ตามที่ถาม) ในแต่ละโซลูชันให้ใช้สิ่งนี้ภายในลูป:

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

ทำงานกับไดเรกทอรีที่มีช่องว่าง

แรงบันดาลใจจากSorpigal

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

9
find . -mindepth 1 -maxdepth 1 -type d -printf "%P\n"

basenameที่มีความสุขมากเกินไปและลดความจำเป็นในการ ฉันต้องการคำตอบมากกว่านี้
Boldewyn

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ยกตัวอย่างเช่นฉันต้องการที่จะเปลี่ยนเป็นทุกไดเรกทอรีย่อยและเรียกใช้คำสั่ง ดังนั้นฉันใช้ (จากWikipedia ):

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แต่ปลอดภัยสำหรับไดเรกทอรีที่มีช่องว่างในชื่อ นอกจากนี้สายที่แยกต่างหากเพื่อจะทำในเปลือกเดียวกันซึ่งเป็นเหตุผลในคำสั่งของฉันเราจะต้องกลับไปยังไดเรกทอรีปัจจุบันด้วย stuffpopd


3

bash loop ที่น้อยที่สุดที่คุณสามารถสร้างได้ (จากคำตอบ ghostdog74)

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

เพื่อซิปไฟล์ทั้งไฟล์ตามไดเรกทอรี

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

รายการนี้แสดงไฟล์ทั้งหมดของdirectoryไม่เพียงไดเรกทอรีย่อย
dexteritas


2

หากคุณต้องการรันคำสั่งหลายคำสั่งในลูป for คุณสามารถบันทึกผลลัพธ์findด้วยmapfile(bash> = 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.