ไดเรกทอรีที่มีสองไฟล์ขึ้นไป


11

ฉันต้องการค้นหาไดเรกทอรีย่อยของไดเรกทอรีปัจจุบันซึ่ง (นั่นคือไดเรกทอรีย่อย) มีไฟล์ปกติอย่างน้อย 2 ไฟล์

ฉันไม่สนใจไดเรกทอรีที่มีไฟล์น้อยกว่า 2 ไฟล์และไม่ได้อยู่ในไดเรกทอรีที่มีไดเรกทอรีย่อยเท่านั้น

คำตอบ:


12

นี่คือวิธีการที่แตกต่างกันอย่างสมบูรณ์ขึ้นอยู่กับ GNU และfind uniqสิ่งนี้เร็วกว่าและเป็นมิตรกับ CPU มากกว่าคำตอบโดยขึ้นอยู่กับการดำเนินการคำสั่งเชลล์ที่นับจำนวนไฟล์สำหรับแต่ละไดเรกทอรีที่พบ

find . -type f -printf '%h\n' | sort | uniq -d

findคำสั่งพิมพ์ไดเรกทอรีของไฟล์ทั้งหมดในลำดับชั้นและuniqแสดงเฉพาะไดเรกทอรีที่ปรากฏอย่างน้อยสองครั้ง


2
findคุณไม่ควรแยกการส่งออกของ ในกรณีนี้เนื่องจาก GNU findจะรวมชื่อไดเรกทอรีที่มีอักขระที่ไม่สามารถพิมพ์ได้ในภาษาปัจจุบัน (เช่น "ä" ในภาษา C) ดูเพิ่มเติมที่unix.stackexchange.com/questions/321697/…
Kusalananda

4
@ Kusalananda ไม่ใช่เมื่อผลลัพธ์ไม่ถึง tty ที่นี่ปัญหาเดียวคือตัวละครขึ้นบรรทัดใหม่ซึ่งคุณสามารถแก้ไขได้โดยใช้-printf '%h\0' | sort -z | uniq -zd | xargs -r0 ...
Stéphane Chazelas

6
find . -type d \
    -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' \
    -print

นี่จะค้นหาชื่อทั้งหมดในหรือภายใต้ไดเรกทอรีปัจจุบันแล้วกรองชื่อทั้งหมดที่ไม่ใช่ชื่อของไดเรกทอรี

ชื่อไดเรกทอรีที่เหลือจะถูกมอบให้กับสคริปต์สั้น ๆ นี้:

c=0
for n in "$1"/*; do
    [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 ))
done

[ "$c" -ge 2 ]

สคริปต์นี้จะนับจำนวนไฟล์ปกติ (ข้ามลิงก์สัญลักษณ์) ในไดเรกทอรีที่กำหนดเป็นอาร์กิวเมนต์บรรทัดคำสั่งแรก (จากfind) คำสั่งสุดท้ายในสคริปต์คือการทดสอบเพื่อดูว่าการนับเป็น 2 หรือมากกว่า ผลลัพธ์ของการทดสอบนี้คือค่าส่งคืน (สถานะออก) ของสคริปต์

หากการทดสอบสำเร็จ-printจะทำให้findพิมพ์เส้นทางไปยังไดเรกทอรี

หากต้องการพิจารณาไฟล์ที่ซ่อนอยู่ (ไฟล์ที่ชื่อขึ้นต้นด้วยจุด) ให้เปลี่ยนsh -cสคริปต์จากคำพูด

for n in "$1"/*; do

ถึง

for n in "$1"/* "$1"/.*; do

การทดสอบ:

$ tree
.
`-- test
    |-- a
    |-- dir1
    |   |-- a
    |   |-- b
    |   `-- c
    `-- dir2
        |-- dira
        |-- dirb
        |   |-- file-1
        |   `-- file-2
        `-- dirc

6 directories, 6 files

$ find . -type d -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' -print
./test/dir1
./test/dir2/dirb

โซลูชันของคุณจะไม่นับไฟล์ที่มีชื่อขึ้นต้นด้วยจุด คุณควรเริ่มต้น c = 0 เพื่อหลีกเลี่ยงข้อความแสดงข้อผิดพลาดกับไดเรกทอรีที่ไม่มีไฟล์ใด ๆ
xhienne

@ xhienne ฉันถือว่าไฟล์ที่ซ่อนอยู่และจะเพิ่มบันทึกเกี่ยวกับมัน ไม่มีข้อผิดพลาดหากไม่มีไฟล์ปกติในไดเรกทอรีเนื่องจาก[ "" -ge 2 ]เป็นการทดสอบที่ถูกต้อง
Kusalananda

ไม่แน่ใจว่าคุณกำหนด "ถูกต้อง" ได้อย่างไร POSIX ต้องการ arg1 เป็นค่าจำนวนเต็ม dash, bash --posixและtestจอแสดงผลทุกข้อผิดพลาดและออกมี 2 (คือ "เกิดข้อผิดพลาด")
xhienne

@ xhienne Ah ฉันกำลังทดสอบระบบที่ mas kshกำลังทำงานshอยู่ จะแก้ไขทันที ขอบคุณสำหรับ poking ที่ฉัน! :-)
Kusalananda

นอกจากนี้ยังมี[ -f ... ]ลิงก์ที่เป็นสัญลักษณ์ คุณควรเพิ่มการทดสอบเพื่อกำจัดเนื่องจากคำถามระบุว่าควรนับเฉพาะไฟล์ปกติเท่านั้น
xhienne

6

ด้วยความช่วยเหลือของคำตอบของ Gillesในเรื่องSUและการย้อนกลับและการดัดแปลงบางอย่างนี่คือสิ่งที่คุณต้องการ

find . -type d -exec sh -c 'set -- "$1"/*;X=0; 
    for args; do [ -f "$args" ] && X=$((X+1)) ;done; [ "$X" -gt 1 ] ' _ {} \; -print

ต้นไม้ไดเรกทอรี

.
├── test
│   ├── dir1
│   │   ├── a
│   │   ├── b
│   │   └── c
│   ├── dir2
│   │   ├── dira
│   │   │   └── a file\012with\012multiple\012line
│   │   ├── dirb
│   │   │   ├── file-1
│   │   │   └── file-2
│   │   └── dirc
│   ├── diraa
│   ├── dirbb
│   ├── dircc
│   └── x
│   └── x1
│   └── x2
└── test2
    ├── dir3
    └── dir4

ผลลัพธ์:

./test
./test/dir1
./test/dir2/dirb

ฉันมีสิ่งนี้ในตอนแรก แต่คุณจะมีปัญหากับไดเรกทอรีที่มีหลายไดเรกทอรีย่อยและไฟล์ นอกจากนี้ยังไม่ได้แยกไดเรกทอรีที่มีไดเรกทอรีย่อยเท่านั้น
Kusalananda

มันไม่ได้แก้ปัญหาจริงๆ พบทั้งtestและdir2ไดเรกทอรีในการตั้งค่าการทดสอบของฉัน (ดูคำตอบของฉัน)
Kusalananda

ใช้งานได้สำหรับตัวอย่างของคุณ แต่เพิ่มtest/x1และtest/x2เป็นไฟล์เช่น ... $1และ$2จะเป็นไดเรกทอรีสำหรับtestและไดเรกทอรีจะพลาด
Kusalananda

@ Kusalananda ไม่มีทางที่ฉันพบยกเว้นสิ่งที่คุณตอบฉันพยายามที่จะเปลี่ยนบางส่วนของคำสั่งของฉันที่จะไม่ซ้ำกันแน่นอนของคุณ (ฉันไม่ได้ยกเว้นไฟล์ที่ซ่อนอยู่ตามที่คุณทำ) ขอโทษของฉัน
αғsнιη

1
ไม่ต้องกังวล :-)
Kusalananda

3

อีกวิธีfind+ wc:

find path/currdir -maxdepth 1 -type d ! -empty ! -path "path/currdir" \
-exec sh -c 'count=$(find "$1" -maxdepth 1 -type f | wc -l); [ $count -ge 2 ]' _ {} \; -print

  • path/currdir - พา ธ ไปยังไดเรกทอรีปัจจุบันของคุณ

  • -maxdepth 1- พิจารณาเฉพาะโฟลเดอร์ย่อยของเด็กโดยตรง

  • ! -empty - ละเว้นโฟลเดอร์ย่อยที่ว่างเปล่า

  • ! -path "path/currdir" - ละเว้นเส้นทางไดเรกทอรีปัจจุบัน

  • count=$(find "$1" -maxdepth 1 -type f | wc -l)- countถูกกำหนดด้วยจำนวนไฟล์สำหรับแต่ละโฟลเดอร์ย่อยที่พบ

  • [ $count -ge 2 ] ... -print - พิมพ์ชื่อ / พา ธ ของโฟลเดอร์ย่อยที่มีไฟล์ปกติอย่างน้อย 2 ไฟล์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.