ฉันต้องการตรวจสอบไดเรกทอรีย่อยทั้งหมดและรายงานจำนวนไฟล์ (โดยไม่ต้องเรียกซ้ำ) เพิ่มเติมประกอบด้วย:
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
สำรวจทั้งสองไดเรกทอรีย่อยที่ระบุไปยัง-maxdepth
1 ซึ่งป้องกันการเกิดซ้ำอีกและรายงานเฉพาะไฟล์ ( -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
, และxargs
awk
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 ที่ซ่อนอยู่, ไม่รวม.
และ..
)