ฉันต้องการตรวจสอบไดเรกทอรีย่อยทั้งหมดและรายงานจำนวนไฟล์ (โดยไม่ต้องเรียกซ้ำ) เพิ่มเติมประกอบด้วย:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
ฉันต้องการตรวจสอบไดเรกทอรีย่อยทั้งหมดและรายงานจำนวนไฟล์ (โดยไม่ต้องเรียกซ้ำ) เพิ่มเติมประกอบด้วย:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
คำตอบ:
วิธีนี้ทำได้ในวิธีที่ปลอดภัยและพกพาได้ มันจะไม่สับสนกับชื่อไฟล์แปลก ๆ
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
โปรดทราบว่ามันจะพิมพ์จำนวนไฟล์ก่อนจากนั้นชื่อไดเรกทอรีในบรรทัดแยกต่างหาก หากคุณต้องการรักษารูปแบบของ OP คุณจะต้องจัดรูปแบบเพิ่มเติมเช่น
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
หากคุณมีไดเรกทอรีย่อยชุดหนึ่งที่คุณสนใจคุณสามารถแทนที่*มันได้
ทำไมจึงปลอดภัย (และดังนั้นจึงมีค่าสคริปต์)
ชื่อไฟล์สามารถมีตัวอักษรใด ๆ /ยกเว้น มีอักขระบางตัวที่ได้รับการปฏิบัติเป็นพิเศษไม่ว่าจะโดยเชลล์หรือโดยคำสั่ง สิ่งเหล่านั้นรวมถึงช่องว่างการขึ้นบรรทัดใหม่และขีดกลาง
การใช้for f in *โครงสร้างเป็นวิธีที่ปลอดภัยในการรับชื่อไฟล์แต่ละชื่อ
find $fเมื่อคุณมีชื่อไฟล์ในตัวแปรที่คุณยังคงต้องหลีกเลี่ยงสิ่งที่ต้องการ ถ้า$fมีชื่อไฟล์-test, findจะบ่นเกี่ยวกับตัวเลือกที่คุณเพียงแค่ให้มัน วิธีการหลีกเลี่ยงปัญหานั้นคือการใช้./ชื่อต่อหน้า; วิธีนี้มีความหมายเหมือนกัน แต่ไม่เริ่มต้นด้วยเส้นประอีกต่อไป
การขึ้นบรรทัดใหม่และช่องว่างก็เป็นปัญหาเช่นกัน ถ้า$fมี "สวัสดีเพื่อน" เป็นชื่อไฟล์เป็นfind ./$f find ./hello, buddyคุณบอกfindไปดูที่และ./hello, หากผู้ที่ไม่อยู่ก็จะบ่นและมันจะไม่เคยมองในbuddy ./hello, buddyสิ่งนี้ง่ายต่อการหลีกเลี่ยง - ใช้เครื่องหมายคำพูดรอบตัวแปรของคุณ
สุดท้ายชื่อไฟล์อาจมีการขึ้นบรรทัดใหม่ดังนั้นการนับบรรทัดใหม่ในรายการชื่อไฟล์จะไม่ทำงาน คุณจะได้รับการนับเพิ่มสำหรับชื่อไฟล์ทุกรายการด้วยการขึ้นบรรทัดใหม่ เพื่อหลีกเลี่ยงปัญหานี้อย่านับบรรทัดใหม่ในรายการไฟล์ ให้นับการขึ้นบรรทัดใหม่ (หรืออักขระอื่น ๆ ) แทนไฟล์เดียว นี้คือเหตุผลที่findคำสั่งมีเพียงไม่-exec echo \; -exec echo {} \;ฉันต้องการพิมพ์บรรทัดใหม่เพียงบรรทัดเดียวเพื่อจุดประสงค์ในการรวบรวมไฟล์
-mindepth 1
-printf '\n' -exec echo
-printfแต่ไม่ใช่ถ้าคุณต้องการให้มันทำงานบน FreeBSD
สมมติว่าคุณกำลังมองหาโซลูชัน Linux มาตรฐานวิธีที่ค่อนข้างตรงไปตรงมาในการบรรลุคือfind:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
ในกรณีที่findสำรวจทั้งสองไดเรกทอรีย่อยที่ระบุไปยัง-maxdepth1 ซึ่งป้องกันการเกิดซ้ำอีกและรายงานเฉพาะไฟล์ ( -type f) คั่นด้วยการขึ้นบรรทัดใหม่ ผลลัพธ์จะถูกไพพ์ไปwcยังเพื่อนับจำนวนบรรทัดเหล่านั้น
find . -maxdepth 1 -type dผลลัพธ์ได้อย่างไร
find $dirs ...หรือ (b) หากพวกเขาอยู่ในไดเรกทอรีระดับสูงกว่าหนึ่งเดียว glob จากไดเรกทอรีนั้นfind */ ...
-exec echoไปยังคำสั่ง find ของคุณ - ด้วยวิธีนี้มันไม่ได้สะท้อนชื่อไฟล์เพียงขึ้นบรรทัดใหม่
โดย“ ไม่เรียกซ้ำ” หมายความว่าหากdirectoryName1มีไดเรกทอรีย่อยคุณไม่ต้องการนับไฟล์ในไดเรกทอรีย่อยหรือไม่ ถ้าเป็นเช่นนั้นต่อไปนี้เป็นวิธีนับไฟล์ปกติทั้งหมดในไดเรกทอรีที่ระบุ:
count=0
for d in directoryName1 directoryName2; do
for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
if [ -f "$f" ]; then count=$((count+1)); fi
done
done
โปรดทราบว่าการ-fทดสอบดำเนินการสองฟังก์ชั่น: ทดสอบว่ารายการที่จับคู่โดยหนึ่งใน globs ด้านบนเป็นไฟล์ปกติหรือไม่และทดสอบว่ารายการนั้นเป็นการจับคู่หรือไม่ (ถ้าหนึ่งใน globs ตรงกับสิ่งใด หากคุณต้องการที่จะนับรายการทั้งหมดในไดเรกทอรีที่กำหนดโดยไม่คำนึงถึงประเภทของพวกเขาแทนที่ด้วย-f-e
Ksh มีวิธีทำให้รูปแบบจับคู่ไฟล์จุดและสร้างรายการว่างในกรณีที่ไม่มีไฟล์ตรงกับรูปแบบ ดังนั้นใน ksh คุณสามารถนับไฟล์ปกติเช่นนี้ได้:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
หรือไฟล์ทั้งหมดเช่นนี้
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
Bash มีวิธีที่แตกต่างกันในการทำให้สิ่งนี้ง่ายขึ้น วิธีนับไฟล์ปกติ:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
วิธีนับไฟล์ทั้งหมด:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
ตามปกติมันจะง่ายกว่าใน zsh วิธีนับไฟล์ปกติ:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
เปลี่ยน(DN.)เป็น(DN)นับไฟล์ทั้งหมด
¹ โปรดทราบว่าแต่ละรูปแบบจะจับคู่กันมิฉะนั้นผลลัพธ์อาจถูกปิด (เช่นหากคุณกำลังนับไฟล์ที่ขึ้นต้นด้วยตัวเลขคุณไม่สามารถทำได้for x in [0-9]*; do if [ -f "$x" ]; then …เพราะอาจมีไฟล์ชื่อ[0-9]foo)
ขึ้นอยู่กับสคริปต์นับ , คำตอบของ Shawnและเคล็ดลับทุบตีเพื่อให้แน่ใจว่าแม้แต่ชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่จะมีการพิมพ์ในรูปแบบที่สามารถใช้งานได้ในบรรทัดเดียว:
for f in *
do
if [ -d "./$f" ]
then
printf %q "$f"
printf %s ' '
find "$f" -maxdepth 1 -printf x | wc -c
fi
done
printf %qคือการพิมพ์สตริงที่อ้างถึงนั่นคือสตริงแบบบรรทัดเดียวซึ่งคุณสามารถใส่ลงในสคริปต์ Bash เพื่อตีความเป็นสตริงตัวอักษรรวมถึงการขึ้นบรรทัดใหม่ (และอาจ) และอักขระพิเศษอื่น ๆ ตัวอย่างเช่นดูVSecho -n $'\tfoo\nbar'printf %q $'\tfoo\nbar'
findคำสั่งทำงานโดยเพียงแค่พิมพ์ตัวอักษรตัวเดียวสำหรับแต่ละไฟล์แล้วนับผู้แทนสายการนับ
นี่ก็เป็น "แรงเดรัจฉาน" วิธี -ish ของคุณที่จะได้รับผลใช้find, echo, ls, wc, และxargsawk
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in *; do echo $i; ls $i | wc -l; done
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
findเมื่อ Bash จะทำอย่างไร(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done): สำหรับทุกไดเรกทอรีให้นับจำนวนรายการในไดเรกทอรีนั้น (รวมถึงไฟล์ dot ที่ซ่อนอยู่, ไม่รวม.และ..)