Bagaimana cara menghapus semua direktori kosong di subtree?

151

Bagaimana saya bisa menghapus semua direktori kosong di subtree? Saya menggunakan sesuatu seperti

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

tapi saya perlu dijalankan beberapa kali untuk menghapus direktori yang berisi direktori kosong saja. Selain itu, ini sangat lambat, terutama di bawah cygwin.

maaartinus
sumber
Lihat juga emacs.stackexchange.com/q/12190/2264 untuk solusi emacs.
Sean Allred

Jawaban:

222

Menggabungkan findopsi dan predikat GNU , perintah ini harus melakukan pekerjaan:

find . -type d -empty -delete
  • -type d membatasi ke direktori
  • -empty membatasi ke yang kosong
  • -delete menghapus setiap direktori

Pohon itu berjalan dari daun tanpa perlu menentukan -depthseperti apa yang tersirat -delete.

Christophe Drevet-Droguet
sumber
2
-deletesudah tersirat -depthsehingga Anda tidak perlu menentukannya secara manual.
jamadagni
1
Terima kasih, saya tidak menyadarinya. Jawaban diperbarui.
Christophe Drevet-Droguet
11
Saya akan menambahkan di -mindepth 1sini, untuk mencegah menghapus direktori awal itu sendiri, jika itu akan kosong.
Greg Dubicki
2
Hebat, tetapi tidak berfungsi pada host SunOS lama saya ...
dokaspar
2
!memiliki arti khusus untuk shell. Anda harus menghindarinya. Sesuatu seperti: \! -name 'Completed'sesaat sebelum -deleteharus bekerja. Atau Anda cukup meletakkan file marker di direktori ini.
Christophe Drevet-Droguet
53

Daftar direktori bersarang-pertama.

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

(Perhatikan bahwa pengalihan berlaku untuk findperintah secara keseluruhan, tidak hanya untuk rmdir. Mengarahkan hanya untuk rmdirakan menyebabkan perlambatan yang signifikan karena Anda harus meminta shell perantara.)

Anda dapat menghindari menjalankan rmdirdirektori yang tidak kosong dengan melewati -emptypredikat untuk menemukannya. GNU menemukan menguji direktori ketika hendak menjalankan perintah, sehingga direktori yang baru saja dikosongkan akan diambil.

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

Cara lain untuk mempercepat adalah mengelompokkan rmdirdoa. Keduanya cenderung lebih cepat dari aslinya, terutama di bawah Cygwin. Saya tidak berharap banyak perbedaan antara keduanya.

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

Metode mana yang lebih cepat tergantung pada berapa banyak direktori kosong yang Anda miliki. Anda tidak dapat menggabungkan -emptydengan metode untuk mengelompokkan doa, karena direktori yang hanya berisi direktori kosong tidak kosong pada saat findmelihatnya.

Metode lain adalah dengan menjalankan beberapa lintasan. Apakah ini lebih cepat tergantung pada banyak hal, termasuk apakah seluruh hierarki direktori dapat tetap ada di cache disk antara proses find.

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

Atau, gunakan zsh. The kualifikasi gumpal F cocok direktori yang tidak kosong, sehingga /^Fcocok direktori kosong. Direktori yang hanya berisi direktori kosong tidak dapat dicocokkan dengan mudah.

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

(Ini berakhir ketika rmdirmenerima baris perintah kosong.)

Gilles
sumber
Itu dia. Alih-alih 90 detik, dibutuhkan 0,90 detik.
maaartinus
@maaartinus: Saya ingin tahu: apakah Anda memiliki kumpulan data serupa di mana Anda bisa mencobanya tanpa -p? Saya tidak akan berpikir itu akan membuat perbedaan.
Gilles
3
@maartinus - optimisasi kecil lainnya: menambahkan -emptyharus bekerja dengan yang satu ini (walaupun saya tidak yakin persis berapa banyak yang akan didapat). Dan sangat, sangat sepele, karena Anda mungkin tidak ingin menghapus ., gunakan -mindepth 1.
mattdm
Itu bukan pemindahan tetapi proses memulai di atas kepala, yang mengambil hampir sepanjang waktu. Saya telah mengabaikan -depthargumen, yang membuat rmdir -ptidak berguna. Saya sudah mengubah komentar saya. 90-an adalah upaya awal saya; tidak ada yang mengejutkan di sini.
maaartinus
2
Saya menyadari bahwa kita dapat menghapus rmdirpanggilan perintah sama sekali, setidaknya dengan GNU find, dengan perintah ini:find . -depth -type d -empty -delete
Christophe Drevet-Droguet
6

Jika Anda hanya menempelkannya -ppada Anda rmdir, itu akan bekerja dalam satu pass. Tidak akan cantik atau optimal, tetapi harus mendapatkan segalanya. Itu memberitahu rmdir untuk menghapus direktori induk yang tidak kosong dari direktori yang Anda hapus.

Anda dapat menyimpan sedikit dengan menambahkan -emptytes untuk menemukan, sehingga tidak mengganggu dengan direktori yang tidak kosong.

mattdm
sumber
3

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

adalah jawaban yang paling sederhana dan sesuai standar untuk pertanyaan ini.

Sayangnya, jawaban lain yang diberikan di sini tergantung pada perangkat tambahan khusus vendor yang tidak ada pada semua sistem.

schily
sumber
3
Jawaban ini menimbulkan kesalahan untuk setiap direktori yang tidak dapat dihapus yang mungkin kurang dari yang diinginkan.
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

Begini cara kerjanya:

  1. Secara rekursif daftarkan semua direktori beserta kedalamannya
  2. Urutkan berdasarkan urutan kedalamannya
  3. Memfilter hanya jalur direktori
  4. Jalankan rmdirpada daftar satu per satu
Ashish Ranjan
sumber
0

Saya menggunakan alias ini untuk findperintah yang sering digunakan , terutama ketika saya membersihkan ruang disk menggunakan dupeguru , di mana menghapus duplikat dapat menghasilkan banyak direktori kosong.

Komentar di dalam .bashrcjadi saya tidak akan melupakannya nanti ketika saya perlu mengubahnya.

# 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 {} +'
raychi
sumber
-2

rm -r */perintah bekerja dengan mudah untukku. rmharus mengharuskan -funtuk menghapus direktori dengan file dengan paksa. rm -rseharusnya hanya menghapus direktori kosong. Saya terbuka mengapa ini bisa salah. Ini juga harus meninggalkan file karena */hanya melihat folder.

libroman2
sumber
1
Saya sangat merekomendasikan untuk mengujinya terlebih dahulu karena rmterutama dimaksudkan untuk menghapus file. Meskipun */hanya cocok dengan direktori, saya tidak tahu apa fungsinya di level yang lebih dalam. Saya juga bisa membayangkan bahwa itu hanya bekerja pada beberapa sistem.
maaartinus