2009-08-17 29 views
8

Tôi đang cố gắng viết một số ruby ​​sẽ đệ quy tìm kiếm một thư mục nhất định cho tất cả thư mục con trống và xóa chúng.Ruby: làm cách nào để đệ quy tìm và xóa thư mục trống?

Suy nghĩ?

Lưu ý: Tôi muốn có phiên bản tập lệnh nếu có thể. Đây là cả một nhu cầu thiết thực và một cái gì đó để giúp tôi học hỏi.

+1

suy nghĩ đầu tiên: hệ thống "tìm. -type d | xargs rmdir -p 2>/dev/null" – kch

+0

Chỉ cần lưu ý, tôi không muốn thực hiện thao tác này trong một lần chụp từ dòng lệnh. Nó sẽ được trong một kịch bản ruby. Những gì bạn có ở trên là một phiên bản dòng cmd không? –

+0

tốt, đó là một lệnh shell, có, nhưng được gọi từ bên trong ruby ​​bằng cách sử dụng 'Kernel.system';) – kch

Trả lời

15

Trong ruby:

Dir['**/*']           \ 
    .select { |d| File.directory? d }     \ 
    .select { |d| (Dir.entries(d) - %w[ . .. ]).empty? } \ 
    .each { |d| Dir.rmdir d } 
+0

bạn có thể giải thích "-" trong dòng 3 không? –

+0

Tôi sẽ trừ mảng chứa chuỗi "." và ".." từ mảng các mục nhập thư mục, vì mỗi thư mục chứa hai mục nhập đặc biệt đó. – kch

+0

cũng có, bạn có thể giải thích "\" là gì không? tiếp tục dòng? họ có cần không? –

2

Tại sao không chỉ sử dụng trình bao?

tìm. -type d -empty -exec rmdir '{}' \;

Thực hiện chính xác những gì bạn muốn.

+0

Tôi đã hy vọng cho một phiên bản script ruby ​​để giúp nắm bắt làm việc với các tập tin. Tôi có thể gọi ở trên với 'sh ' từ một kịch bản rake tôi giả sử. Bạn có phiên bản tập lệnh? –

+0

Lệnh kịch bản sẽ giống hệt nhau. Tuy nhiên, bài đăng dưới đây có câu trả lời phù hợp với bạn. – koenigdmj

0
Dir.glob('**/*').each do |dir| 
    begin 
    Dir.rmdir dir if File.directory?(dir) 
    # rescue # this can be dangereous unless used cautiously 
    rescue Errno::ENOTEMPTY 
    end 
end 
+0

Giải cứu từ bất kỳ trường hợp ngoại lệ nào không phải là ý tưởng tốt nhất trong mã sản xuất thực. – kch

+0

Thông thường, tôi phải đồng ý, nhưng đây là một tình huống biên giới. Trong mọi trường hợp, tôi đã cập nhật tuyên bố giải cứu. – xyz

+0

Vâng, đó là một trong những điều đó là tốt miễn là bạn biết những gì bạn đang làm. Nhưng một googler đánh trang này có thể đang xem xét nó chỉ cho một kịch bản nhanh chóng, hoặc cho một thư viện sẽ có một vai trò quan trọng trong một ứng dụng tập trung hệ thống tập tin. Vì vậy, về cơ bản, n00bs nhận được mã an toàn theo mặc định, và những người được cho là biết những gì họ đang làm sẽ làm như vậy có nguy cơ của riêng họ. – kch

0

Tôi đã thử nghiệm kịch bản này trên OS X, nhưng nếu bạn đang ở trên Windows, bạn sẽ cần phải thực hiện thay đổi.

Bạn có thể tìm thấy các tệp trong thư mục, bao gồm các tệp ẩn, có mục nhập Dir #.

Mã này sẽ xóa các thư mục bị trống khi bạn đã xóa bất kỳ thư mục con nào.

def entries(dir) 
    Dir.entries(dir) - [".", ".."] 
end 

def recursively_delete_empty(dir) 
    subdirs = entries(dir).map { |f| File.join(dir, f) }.select { |f| File.directory? f } 
    subdirs.each do |subdir| 
    recursively_delete_empty subdir 
    end 

    if entries(dir).empty? 
    puts "deleting #{dir}" 
    Dir.rmdir dir 
    end 
end 
1
module MyExtensions 
    module FileUtils 
    # Gracefully delete dirs that are empty (or contain empty children). 
    def rmdir_empty(*dirs) 
     dirs.each do |dir| 
     begin 
      ndel = Dir.glob("#{dir}/**/", File::FNM_DOTMATCH).count do |d| 
      begin; Dir.rmdir d; rescue SystemCallError; end 
      end 
     end while ndel > 0 
     end 
    end 
    end 

    module ::FileUtils 
    extend FileUtils 
    end 
end 
2
Dir['/Users/path/Movies/incompleteAnime/foom/**/*']. \ 
select { |d| File.directory? d }. \ 
sort.reverse. \ 
each {|d| Dir.rmdir(d) if Dir.entries(d).size == 2} 

giống như ví dụ đầu tiên, nhưng ví dụ đầu tiên dường như không để xử lý các bit đệ quy. Việc sắp xếp và đảo ngược đảm bảo chúng ta đối phó với hầu hết các thư mục lồng nhau đầu tiên.

Tôi cho rằng sort.reverse có thể được viết như sort {|a,b| b <=> a} cho hiệu quả

3

Bạn phải xóa theo thứ tự ngược, nếu không nếu bạn có một foo thư mục trống với quầy bar thư mục con bạn sẽ xóa thanh, nhưng không foo.

Dir.glob(dir + "/**/*").select { |d| 
    File.directory?(d) 
    }.reverse_each { |d| 
    if ((Dir.entries(d) - %w[ . .. ]).empty?) 
     Dir.rmdir(d) 
    end 
    } 
5

Nhìn vào các ví dụ từ kch, dB. và Vishnu ở trên, tôi đã đặt cùng một lớp lót mà tôi nghĩ là một giải pháp thanh lịch hơn:

Dir['**/'].reverse_each { |d| Dir.rmdir d if Dir.entries(d).size == 2 } 

tôi sử dụng '**/' thay vì '/**/*' cho glob, mà chỉ trả về thư mục, vì vậy tôi không có để kiểm tra xem đó có phải là thư mục sau này hay không. Tôi đang sử dụng reverse_each thay vì sort.reverse.each vì nó ngắn hơn và được cho là hiệu quả hơn, theo điều này post. Tôi thích Dir.entries(d).size == 2 đến (Dir.entries(d) - %w[ . .. ]).empty? vì dễ đọc và dễ hiểu hơn, mặc dù (Dir.entries(d) - %w[ . .. ]).empty? có thể hoạt động tốt hơn nếu bạn phải chạy tập lệnh trên Windows.

Tôi đã thử nghiệm điều này khá một chút trên Mac OS X và nó hoạt động tốt, ngay cả với thư mục trống đệ quy.

+0

Một trình biên dịch rất thanh lịch của các giải pháp. – Shadow

+1

Đẹp refactor nhưng tôi nghĩ đến. và .. trực quan hơn kích thước == 2 vì vậy tôi sẽ đi với 'Dir ['lib/** /']. reverse_each {| d | Dir.rmdir d nếu Dir.entries (d) .sort ==% w (. ..)} ' – mahemoff

+0

Vâng, loại đó trực quan hơn. – Adam

Các vấn đề liên quan