Ruby: как мне рекурсивно найти и удалить пустые каталоги?

Я пытаюсь написать какой-нибудь ruby, который будет рекурсивно искать в данном каталоге все пустые дочерние каталоги и удалять их.

Мысли?

Примечание: яМне нравится версия сценария, если это возможно. Это и практическая потребность, и кое-что, чтобы помочь мне учиться.

 kch17 авг. 2009 г., 23:53
Первая мысль: система "находить . тип d | xargs rmdir -p 2>/ DEV / нуль»
 Dane O'Connor17 авг. 2009 г., 23:55
Просто записка, я неЯ не хочу делать эту операцию одним выстрелом из командной строки. Это будет в рубиновом сценарии. То, что у вас есть выше, это версия cmd line no?
 kch18 авг. 2009 г., 00:02
ну, этоs команда оболочки, да, но вызывается из ruby, используяKernel.system ;)

Ответы на вопрос(8)

Решение Вопроса

В рубине:

Dir['**/*']                                            \
  .select { |d| File.directory? d }                    \
  .select { |d| (Dir.entries(d) - %w[ . .. ]).empty? } \
  .each   { |d| Dir.rmdir d }
 Dane O'Connor18 авг. 2009 г., 00:03
не могли бы вы объяснить "-" в строке 3? I '
 kch18 авг. 2009 г., 00:07
Oни'Продолжение линии, да. Oни'Не нужны, если вы ставите точку в конце предыдущей строки, но они мне нравятся в начале, чтобы было ясно, что эта строка продолжается с предыдущей. (Хотя на самом деле, я просто отформатировал как таковой для целей отображения SO. Я обычно оставляю эти вещи в одной строке, до ~ 160 символов или около того. Некоторые люди ненавидят меня, да. Я думаю, что они 'Вы используете TTY.)
 dB.05 апр. 2011 г., 22:18
Вы должны удалить в обратном порядке, иначе вы выигралиудалить все пустые каталоги
 kch18 авг. 2009 г., 00:09
Видите ли, как я написал код, если я уронил \ s, ruby мог бы интерпретировать строку как законченное выражение, и так оно и будет.
 Dane O'Connor18 авг. 2009 г., 00:05
Кроме того, не могли бы вы объяснить, что такое "\"s для? продолжение строки? они нужны?
 Dane O'Connor18 авг. 2009 г., 00:09
ха-ха. Спасибо кч, ты сэкономил мне немного времени. Я вытащил весь документ, но мне не хватало части общей картины, чтобы собрать все вместе.
 kch18 авг. 2009 г., 00:03
m вычитая массив, содержащий строки "." а также "..» из массива записей каталога, так как каждый каталог содержит эти две специальные записи.
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
Dir['/Users/path/Movies/incompleteAnime/foom/**/*']. \
select { |d| File.directory? d }. \
sort.reverse. \
each {|d| Dir.rmdir(d) if Dir.entries(d).size ==  2}

так же, как первый пример, но первый пример неКажется, мы справились с рекурсивным битом. Сортировка и обратная проверка гарантируют, что мы в первую очередь имеем дело с самыми вложенными каталогами.

Я полагаю, что sort.reverse можно записать какsort {|a,b| b a} для эффективности

Глядя на примеры из кч, дБ. и Вишну выше, яМы собрали однострочник, который я считаю более элегантным решением:

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

я использую'**/' вместо'/**/*' для шара, который возвращает только каталоги, поэтому я нене нужно проверять,Каталог позже. Я'м используюreverse_each вместоsort.reverse.each как это'короче и предположительно более эффективный, в соответствии с этимсообщение, я предпочитаюDir.entries(d).size == 2 в(Dir.entries(d) - %w[ . .. ]).empty? потому что это'немного легче читать и понимать, хотя(Dir.entries(d) - %w[ . .. ]).empty? вероятно, будет работать лучше, если бы вам пришлось запустить свой скрипт на Windows.I '

Мы проверили это немного на Mac OS X, и он хорошо работает, даже с рекурсивными пустыми каталогами.

 Shadow22 апр. 2014 г., 07:43
Очень элегантная подборка решений.
 Adam24 февр. 2015 г., 07:35
Да, этот вид более интуитивно понятен.
 mahemoff22 февр. 2015 г., 20:21
Хороший рефакторинг, но я думаю, что упоминание. и .. более интуитивно понятен, чем size == 2, поэтому я бы выбрал `Dir ['Библиотека / ** /»] .reverse_each {| d | Dir.rmdir d if Dir.entries (d) .sort ==% w (. ..)} `

Почему бы просто не использовать оболочку?

находить . -type d -empty -exec rmdir '{}» \;

Делает именно то, что вы хотите.

 koenigdmj18 авг. 2009 г., 00:10
Команда сценария будет точно такой же. Тем не менее, ниже пост имеет ответ, который вас устроит.
 Dane O'Connor17 авг. 2009 г., 23:58
Я надеялся на версию ruby-скрипта, которая поможет понять работу с файлами. Я мог бы ссылаться на выше сш <ваш cmd> ' из сценария граблей, я полагаю. Есть версия скрипта?

мы проверили этот скрипт на OS X, но если вы работаете в Windows, выВам нужно будет внести изменения.

Вы можете найти файлы в каталоге, включая скрытые, с записями Dir #.

Этот код удалит каталоги, которые станут пустыми после того, как вымы удалили все подкаталоги.

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
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
 xyz18 авг. 2009 г., 00:35
Обычно я'Я должен согласиться, но это своего рода пограничная ситуация. В любом случае я обновил заявление о спасении.
 kch18 авг. 2009 г., 00:19
Спасение от любого исключения не является лучшей идеей в реальном производственном коде.
 kch21 авг. 2009 г., 05:31
Да, этоЭто одна из тех вещей, которыехорошо, если вы знаете, что выделаешь Но один гуглер, попадающий на эту страницу, может заглянуть в нее просто для быстрого сценария или для библиотеки, которая 'будет играть главную роль в приложениях, интенсивно использующих файловую систему. Так что, по сути, n00bs получают безопасный код по умолчанию, и люди, которые предположительно знают, что ониОн будет делать это на свой страх и риск.

Вы должны удалить в обратном порядке, в противном случае, если у вас есть пустой каталог foo с панелью подкаталогов, вы удалите панель, но не foo.

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

Ваш ответ на вопрос