ฉันจะวนซ้ำไดเรกทอรีต่างๆใน bash ได้อย่างไร


249

ฉันมีโฟลเดอร์ที่มีไดเรกทอรีและไฟล์บางไฟล์ (บางไฟล์ถูกซ่อนอยู่เริ่มต้นด้วยจุด)

for d in *; do
 echo $d
done

จะวนซ้ำไฟล์ทั้งหมด แต่ฉันต้องการวนซ้ำผ่านไดเรกทอรีเท่านั้น ฉันจะทำอย่างไร

คำตอบ:


332

คุณสามารถระบุเครื่องหมายทับที่ท้ายเพื่อจับคู่ไดเรกทอรีเท่านั้น:

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

7
โปรดทราบว่ามันยังมี symlinks ไปยังไดเรกทอรี
Stéphane Chazelas

1
แล้วคุณจะแยก symlink อย่างไร?
rubo77

3
@ rubo77: คุณสามารถทดสอบได้[[ -L $d ]]ว่า $ d เป็นลิงก์สัญลักษณ์หรือไม่
choroba

1
@AsymLabs ไม่ถูกต้องset -Pมีผลกับคำสั่งที่เปลี่ยนไดเรกทอรีเท่านั้น sprunge.us/TNac
Chris Down

4
@choroba: [[ -L "$f" ]]จะไม่รวมลิงก์ในกรณีนี้ด้วย*/คุณจะต้องตัดเครื่องหมายทับต่อท้ายด้วย[[ -L "${f%/}" ]](ดูทดสอบการเชื่อมโยงด้วยเครื่องหมายทับ )
rubo77

78

คุณสามารถทดสอบด้วย-d:

for f in *; do
    if [ -d "$f" ]; then
        # $f is a directory
    fi
done

นี้เป็นหนึ่งในผู้ประกอบการทดสอบไฟล์


8
โปรดทราบว่ามันจะรวม symlinks ไปยังไดเรกทอรี
Stéphane Chazelas

21
if [[ -d "$f" && ! -L "$f" ]]จะไม่รวม
ลิงก์

จะแตกในไดเรกทอรีว่างเปล่า if [[ "$f" = "*" ]]; then continue; fi
Piskvor

30

ระวังวิธีแก้ปัญหาของ choroba แม้ว่าจะสวยงาม แต่ก็สามารถล้วงเอาพฤติกรรมที่ไม่คาดคิดได้หากไม่มีไดเรกทอรีอยู่ในไดเรกทอรีปัจจุบัน ในสถานะนี้แทนที่จะข้ามforลูป bash จะเรียกใช้ลูปหนึ่งครั้งโดยที่dเท่ากับ*/:

#!/usr/bin/env bash

for d in */; do
    # Will print */ if no directories are available
    echo $d
done

ฉันขอแนะนำให้ใช้สิ่งต่อไปนี้เพื่อป้องกันกรณีนี้:

#!/usr/bin/env bash

for f in *; do
    if [ -d ${f} ]; then
        # Will not run if no directories are available
        echo $f
    fi
done

รหัสนี้จะวนลูปผ่านไฟล์ทั้งหมดในไดเรกทอรีปัจจุบันตรวจสอบว่าfเป็นไดเรกทอรีจากนั้นสะท้อนfถ้าเงื่อนไขส่งคืนจริง หากfมีค่าเท่ากับ*/, echo $fจะไม่ดำเนินการ


3
shopt -s nullglobง่ายมากที่จะ
choroba

21

หากคุณต้องการเลือกไฟล์ที่เฉพาะเจาะจงมากกว่าไดเรกทอรีที่ใช้findและส่งไปที่while read:

shopt -s dotglob
find * -prune -type d | while IFS= read -r d; do 
    echo "$d"
done

ใช้shopt -u dotglobเพื่อแยกไดเรกทอรีที่ซ่อนอยู่ (หรือsetopt dotglob/ unsetopt dotglobใน zsh)

IFS=เพื่อหลีกเลี่ยงการแยกชื่อไฟล์ที่มีหนึ่งใน$IFSตัวอย่างเช่น:'a b'

ดูAsymLabs คำตอบด้านล่างสำหรับfindตัวเลือกเพิ่มเติม


แก้ไข:
ในกรณีที่คุณต้องการสร้างค่าการออกจากภายในลูป while คุณสามารถหลีกเลี่ยงการแบ่งย่อยพิเศษด้วยเคล็ดลับนี้:

while IFS= read -r d; do 
    if [ "$d" == "something" ]; then exit 1; fi
done < <(find * -prune -type d)

2
ฉันพบวิธีแก้ปัญหานี้เมื่อ: stackoverflow.com/a/8489394/1069083
rubo77

11

คุณสามารถใช้ทุบตีบริสุทธิ์สำหรับสิ่งนั้น แต่ควรใช้ find:

find . -maxdepth 1 -type d -exec echo {} \;

(ค้นหาเพิ่มเติมจะรวมไดเรกทอรีที่ซ่อนอยู่)


8
โปรดทราบว่าไม่มีการเชื่อมโยงไปยังไดเรกทอรี คุณสามารถใช้shopt -s dotglobสำหรับการbashที่จะรวมไดเรกทอรีที่ซ่อนอยู่ .ขอแสดงยังจะรวมถึง โปรดทราบด้วยว่า-maxdepthไม่ใช่ตัวเลือกมาตรฐาน ( -pruneคือ)
Stéphane Chazelas

2
dotglobตัวเลือกที่น่าสนใจ แต่เฉพาะกับการใช้งานของdotglob จะรวมถึงไดเรกทอรีที่ซ่อนอยู่ (และ dir ปัจจุบันเช่นกัน)*find .
rubo77

6

สิ่งนี้จะทำเพื่อค้นหาทั้งไดเรกทอรีที่มองเห็นและซ่อนอยู่ภายในไดเรกทอรีทำงานปัจจุบันไม่รวมไดเรกทอรีราก:

การวนซ้ำไดเรกทอรีต่างๆ:

 find -path './*' -prune -type d

เพื่อรวม symlink ในผลลัพธ์:

find -L -path './*' -prune -type d

ทำบางสิ่งบางอย่างกับแต่ละไดเรกทอรี (ยกเว้น symlink):

find -path './*' -prune -type d -print0 | xargs -0 <cmds>

เพื่อแยกไดเรกทอรีที่ซ่อนอยู่:

find -path './[^.]*' -prune -type d

เพื่อดำเนินการหลายคำสั่งในค่าที่ส่งคืน (ตัวอย่างที่มีการวางแผนอย่างมาก):

find -path './[^.]*' -prune -type d -print0 | xargs -0 -I '{}' sh -c \
"printf 'first: %-40s' '{}'; printf 'second: %s\n' '{}'"

แทน 'sh -c' ยังสามารถใช้ 'bash -c' เป็นต้น


จะเกิดอะไรขึ้นถ้า echo '* /' in 'สำหรับ d ใน echo * /' มี, พูดถึง 60,000 ไดเรกทอรี
AsymLabs

คุณจะได้รับข้อผิดพลาด "ไฟล์มากเกินไป" แต่สามารถแก้ไขได้: วิธีหลีกเลี่ยง "ไฟล์ที่เปิดมากเกินไป" ในเดเบียน
rubo77

1
ตัวอย่าง xargs ใช้งานได้กับชุดย่อยของชื่อไดเรกทอรีเท่านั้น 9e.g ผู้ที่มีช่องว่างหรือการขึ้นบรรทัดใหม่จะไม่ทำงาน) ดีกว่าที่จะใช้... -print0 | xargs -0 ...ถ้าคุณไม่ทราบว่าชื่อที่แน่นอนคืออะไร
Anthon

1
@ rubo77 วิธีเพิ่มเติมในการแยกไฟล์ / ไดเรกทอรีที่ซ่อนอยู่และการดำเนินการหลายคำสั่ง - แน่นอนว่าสิ่งนี้สามารถทำได้ด้วยสคริปต์เช่นกัน
AsymLabs

1
ฉันเพิ่มตัวอย่างในคำตอบของฉันที่ด้านล่างวิธีการทำบางสิ่งบางอย่างในแต่ละไดเรกทอรีโดยใช้ฟังก์ชั่นด้วยfind * | while read file; do ...
rubo77

3

คุณสามารถวนซ้ำไดเรกทอรีทั้งหมดรวมถึงไดเรกทอรีที่ซ่อนอยู่ (ขึ้นต้นด้วยจุด) ในหนึ่งบรรทัดและหลายคำสั่งด้วย:

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

หมายเหตุ: การใช้รายการ*/ .*/ทำงานในทุบตี แต่ยังแสดงโฟลเดอร์.และ..ในขณะที่ zsh มันจะไม่แสดงสิ่งเหล่านี้ แต่โยนข้อผิดพลาดหากไม่มีไฟล์ที่ซ่อนอยู่ในโฟลเดอร์

เวอร์ชันที่สะอาดกว่าซึ่งจะรวมไดเรกทอรีที่ซ่อนอยู่และไม่รวม .. / จะอยู่ในตัวเลือก dotglob:

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

(หรือเป็นsetopt dotglobzsh)

คุณสามารถยกเลิกการตั้งค่า dotglob ด้วย

shopt -u dotglob

2

ซึ่งจะรวมถึงเส้นทางที่สมบูรณ์ในแต่ละไดเรกทอรีในรายการ:

for i in $(find $PWD -maxdepth 1 -type d); do echo $i; done

1
หยุดพักเมื่อใช้โฟลเดอร์ที่มีช่องว่าง
Alejandro Sazo

0

ใช้findกับ-execเพื่อวนซ้ำไดเรกทอรีและเรียกใช้ฟังก์ชันในตัวเลือก exec:

dosomething () {
  echo "doing something with $1"
}
export -f dosomething
find -path './*' -prune -type d -exec bash -c 'dosomething "$0"' {} \;

ใช้shopt -s dotglobหรือ shopt -u dotglobเพื่อรวม / แยกไดเรกทอรีที่ซ่อนอยู่


0

รายการนี้แสดงไดเรกทอรีทั้งหมดพร้อมกับจำนวนไดเรกทอรีย่อยในเส้นทางที่กำหนด:

for directory in */ ; do D=$(readlink -f "$directory") ; echo $D = $(find "$D" -mindepth 1 -type d | wc -l) ; done

-1
ls -d */ | while read d
do
        echo $d
done

This is one directory with spaces in the name- แต่ได้รับการแยกวิเคราะห์เป็นหลายรายการ
Piskvor

-5
ls -l | grep ^d

หรือ:

ll | grep ^d

คุณอาจตั้งเป็นนามแฝง


1
น่าเสียดายที่ฉันไม่คิดว่านี่จะตอบคำถามซึ่งก็คือ "ฉันต้องการวนลูปผ่านไดเรคทอรีเท่านั้น" - ซึ่งต่างจากlsคำตอบที่ใช้เล็กน้อยนี้ซึ่งแสดงลิสต์ของไดเรกทอรี
Jeff Schaller

1
เป็นความคิดที่ดีที่จะแยกวิเคราะห์ผลลัพธ์ของls: mywiki.wooledge.org/ParsingLs
codeforester
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.