จะลบไดเรกทอรีว่างทั้งหมดในทรีย่อยได้อย่างไร


151

ฉันจะลบไดเรกทอรีว่างทั้งหมดในทรีย่อยได้อย่างไร ฉันใช้สิ่งที่ชอบ

find . -type d -exec rmdir {} 2>/dev/null \;

แต่ฉันต้องทำงานหลายครั้งเพื่อลบไดเรกทอรีที่มีไดเรกทอรีว่างเท่านั้น ยิ่งกว่านั้นมันค่อนข้างช้าโดยเฉพาะอย่างยิ่งใน cygwin


ดูเพิ่มเติมที่emacs.stackexchange.com/q/12190/2264สำหรับโซลูชัน emacs
ฌอน Allred

คำตอบ:


222

การรวมfindตัวเลือกGNU และเพรดิเคตคำสั่งนี้ควรทำงาน:

find . -type d -empty -delete
  • -type d จำกัด เฉพาะไดเรกทอรี
  • -empty จำกัด เฉพาะสิ่งที่ว่างเปล่า
  • -delete ลบแต่ละไดเรกทอรี

ต้นไม้นั้นเดินจากใบไม้โดยไม่จำเป็นต้องระบุ-depthตามที่บอก-deleteไว้


2
-deleteแสดงนัยแล้ว-depthดังนั้นคุณไม่จำเป็นต้องระบุด้วยตนเอง
jamadagni

1
ขอบคุณฉันไม่ได้ตระหนักถึงสิ่งนั้น อัปเดตคำตอบแล้ว
Christophe Drevet-Droguet

11
ฉันจะเพิ่ม-mindepth 1ที่นี่เพื่อป้องกันการลบไดเรกทอรีเริ่มต้นตัวเองถ้ามันจะว่างเปล่า
Greg Dubicki

2
ดี แต่ไม่ทำงานบนโฮสต์ SunOS เก่าของฉัน ...
dokaspar

2
!มีความหมายพิเศษสำหรับเชลล์ คุณต้องหลบหนี สิ่งที่ต้องการ: \! -name 'Completed'ก่อนหน้านี้-deleteควรทำงาน หรือคุณเพียงแค่ใส่ไฟล์เครื่องหมายในไดเรกทอรีนี้
Christophe Drevet-Droguet

53

รายการไดเรกทอรีที่ซ้อนกันลึกก่อน

find . -depth -type d -exec rmdir {} \; 2>/dev/null

(โปรดทราบว่าการเปลี่ยนเส้นทางใช้กับfindคำสั่งโดยรวมไม่ใช่เพื่อการrmdirเปลี่ยนเส้นทางเพียงอย่างเดียวrmdirจะทำให้การทำงานช้าลงอย่างมากเนื่องจากคุณต้องเรียกใช้เชลล์ระดับกลาง)

คุณสามารถหลีกเลี่ยงการรันrmdirบนไดเร็กทอรีที่ไม่ว่างโดยส่งเพรดิเคต-emptyเพื่อค้นหา GNU ค้นหาการทดสอบไดเรกทอรีเมื่อมันกำลังจะเรียกใช้คำสั่งดังนั้นไดเรกทอรีที่เพิ่งถูกลบจะถูกหยิบขึ้นมา

find . -depth -type d -empty -exec rmdir {} \;

อีกวิธีในการเร่งความเร็วก็คือจัดกลุ่มrmdirคำร้อง ทั้งสองมีแนวโน้มว่าจะเร็วกว่าเดิมอย่างเห็นได้ชัดโดยเฉพาะอย่างยิ่งภายใต้ Cygwin ฉันไม่คาดหวังความแตกต่างระหว่างสองสิ่งนี้มากนัก

find . -depth -type d -print0 | xargs -0 rmdir 2>/dev/null
find . -depth -type d -exec rmdir {} + 2>/dev/null

วิธีที่เร็วกว่านั้นขึ้นอยู่กับว่าคุณมีไดเรกทอรีที่ไม่ว่างจำนวนเท่าใด คุณไม่สามารถรวม-emptyกับวิธีสำหรับการจัดกลุ่มการเรียกใช้เนื่องจากไดเรกทอรีที่มีไดเรกทอรีที่ว่างเปล่าเท่านั้นจะไม่ว่างเปล่าตามเวลาที่findดู

อีกวิธีหนึ่งก็คือการวิ่งผ่านหลายรอบ สิ่งนี้เร็วกว่าหรือไม่ขึ้นอยู่กับหลาย ๆ อย่างรวมถึงลำดับชั้นของไดเรกทอรีทั้งหมดที่สามารถอยู่ในดิสก์แคชระหว่างการfindรันได้หรือไม่

while [ -n "$(find . -depth -type d -empty -print -exec rmdir {} +)" ]; do :; done

หรือใช้ zsh ตัวระบุคุณสมบัติกลม Fจับคู่กับไดเรกทอรีที่ไม่ว่างดังนั้นจึง/^Fจับคู่ไดเรกทอรีว่าง ไดเรกทอรีที่มีเฉพาะไดเรกทอรีที่ว่างเปล่าไม่สามารถจับคู่ได้อย่างง่ายดาย

while rmdir **/*(/N^F); do :; done

(สิ่งนี้จะสิ้นสุดเมื่อrmdirได้รับบรรทัดคำสั่งว่าง)


แค่นั้นแหละ. แทนที่จะ 90 วินาทีใช้เวลา 0.90 วินาที
maaartinus

@maaartinus: ฉันอยากรู้: คุณมีชุดข้อมูลที่คล้ายกันซึ่งคุณสามารถลองได้-pหรือไม่? ฉันไม่คิดว่ามันจะสร้างความแตกต่าง
Gilles

3
@maartinus - การเพิ่มประสิทธิภาพเล็ก ๆ น้อย ๆ อื่น ๆ : การเพิ่ม-emptyควรทำงานกับอันนี้ได้ (แม้ว่าฉันไม่แน่ใจว่าจะได้รับเท่าใด) และมากนิด ๆ เพราะคุณอาจไม่ต้องการที่จะลบการใช้งาน. -mindepth 1
mattdm

มันไม่ได้เป็นการลบ แต่กระบวนการเริ่มต้นขึ้นเหนือสิ่งที่เอาเกือบตลอดเวลา ฉันมองข้าม-depthข้อโต้แย้งซึ่งทำให้rmdir -pไร้ประโยชน์ ฉันเปลี่ยนความคิดเห็นแล้ว 90 s เป็นความพยายามดั้งเดิมของฉัน ไม่มีอะไรน่าแปลกใจที่นี่
maaartinus

2
ฉันรู้ว่าเราสามารถลบการrmdirเรียกคำสั่งทั้งหมดอย่างน้อยด้วย GNU find ด้วยคำสั่งนี้:find . -depth -type d -empty -delete
Christophe Drevet-Droguet

6

หากคุณเพียงแค่ตะปู-pบนของคุณrmdirที่จะทำงานในรอบเดียว มันจะไม่สวยหรือดีที่สุด แต่ควรได้ทุกอย่าง ซึ่งบอกให้ rmdir ลบไดเรกทอรีพาเรนต์ที่ไม่ว่างของไดเรกทอรีที่คุณกำลังลบ

คุณสามารถบันทึกได้เล็กน้อยโดยเพิ่มการ-emptyทดสอบเพื่อค้นหาดังนั้นจึงไม่ต้องกังวลกับไดเรกทอรีที่ไม่ว่างเปล่า


3

find . -depth -type d -exec rmdir {} +

เป็นคำตอบที่ง่ายและได้มาตรฐานสำหรับคำถามนี้

คำตอบอื่น ๆ ที่ให้ไว้ที่นี่น่าเสียดายทั้งหมดขึ้นอยู่กับการปรับปรุงเฉพาะของผู้ขายที่ไม่มีอยู่ในทุกระบบ


3
คำตอบนี้ทำให้เกิดข้อผิดพลาดสำหรับแต่ละไดเรกทอรีที่ไม่สามารถลบได้ซึ่งอาจน้อยกว่าที่ต้องการ
Willem van Ketwich

0

find . -type d -printf "%d %p\n" |\ sort -nr |\ perl -pe 's/^\d+\s//;' |\ while read dir; do \ (rmdir "$dir" > /dev/null 2>&1); \ done

นี่คือวิธีการทำงาน:

  1. ทำรายการไดเรกทอรีทั้งหมดซ้ำพร้อมกับความลึก
  2. จัดเรียงตามลำดับความลึก
  3. กรองเฉพาะเส้นทางไดเรกทอรี
  4. ทำงานrmdirในรายการหนึ่งโดยหนึ่ง

0

ฉันใช้นามแฝงเหล่านี้สำหรับfindคำสั่งที่ใช้บ่อยโดยเฉพาะอย่างยิ่งเมื่อฉันล้างพื้นที่ดิสก์โดยใช้dupeguruซึ่งการลบรายการที่ซ้ำกันอาจส่งผลให้เกิดไดเรกทอรีว่างจำนวนมาก

ความเห็นด้านใน.bashrcดังนั้นฉันจะไม่ลืมพวกเขาในภายหลังเมื่อฉันต้องการปรับแต่ง

# find empty directories
alias find-empty='find . -type d -empty'

# fine empty/zero sized files
alias find-zero='find . -type f -empty'

# delete all empty directories!
alias find-empty-delete='find-empty -delete'

# delete empty directories when `-delete` option is not available.
# output null character (instead of newline) as separator. used together
# with `xargs -0`, will handle filenames with spaces and special chars.
alias find-empty-delete2='find-empty -print0 | xargs -0 rmdir -p'

# alternative version using `-exec` with `+`, similar to xargs.
# {}: path of current file
# +: {} is replaced with as many pathnames as possible for each invocation.
alias find-empty-delete3='find-empty -exec rmdir -p {} +'

# for removing zero sized files, we can't de-dupe them automatically
# since they are technically all the same, so they are typically left
# beind. this removes them if needed.
alias  find-zero-delete='find-zero -delete'
alias find-zero-delete2='find-zero -print0 | xargs -0 rm'
alias find-zero-delete3='find-zero -exec rm {} +'

-2

rm -r */คำสั่งทำงานได้อย่างง่ายดายสำหรับฉัน rmควรบังคับ-fให้ลบไดเรกทอรีพร้อมไฟล์ rm -rควรลบไดเรกทอรีที่ว่างเปล่าเท่านั้น ฉันเปิดให้ทำไมสิ่งนี้อาจผิด สิ่งนี้ควรปล่อยให้ไฟล์ไว้เพราะ*/ดูเฉพาะโฟลเดอร์


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