Невозможно принудительно удалить каталог - PullRequest
1 голос
/ 13 апреля 2020

Я использую утилиты Info-ZIP в сценарии Ruby на Windows 10, чтобы разархивировать архив, отредактировать его содержимое и разархивировать. Сценарий предназначен для перебора пакета архивов и удаления временной папки, которая создается при извлечении содержимого. Папка, однако, не удаляется. Например:

archives.each { |archive|
    system("unzip.exe -o archive -d temp")
    [...]
    system("zip.exe -X0q archive .")
    FileUtils.rm_rf "temp"
}

Это всегда работало на Ma c очень хорошо (используя тот же сценарий, в сочетании с командами zip / unzip), однако в Windows я не могу получить временная папка для удаления. Процесс распаковки и архивирования работает нормально, но папка «temp» не будет удалена. Это приводит к тому, что утилита распаковки выдает ту же ошибку: error: cannot delete old temp/[file] для каждого файла, который существует в папке.

Я пытался использовать system("del /Q temp"), который выдает ошибку Could Not Find: C:\[...]\temp, даже если каталог существует. Я попытался system("rmdir /s /q temp"), что выдает еще одну ошибку: The process cannot access the file because it is being used by another process. Единственный «процесс», использующий этот файл, - это сам скрипт, хотя. затем он работает и успешно удаляет каталог. Однако мне нужно, чтобы это было сделано после каждой итерации и внутри одного и того же исходного сценария, чтобы каталог был правильно перезаписан и удален в конце выполнения, без каких-либо ошибок или предупреждений в командной строке.

Is Есть ли другой способ принудительно удалить эту папку?

Обновление: После гораздо большего тестирования различных частей сценария я смог определить точный источник проблемы. Таким образом, все архивы содержат файлы X HTML. В некоторых случаях сценарий требует дублирования архива, а содержимое дублированного архива изменяется. Необходимость создания дубликата зависит от наличия определенной разметки в файле X HTML. Скрипт использует Nokogiri для анализа содержимого. Кажется, что метод анализа через Nokogiri - то, что вызывает проблему. Чтобы упростить код:

FileUtils.cp(original_archive,new_archive)
unzip_archive(new_archive) # a function to contain the unzipping steps
Dir.glob("temp/**/*.{html,xhtml}").each { |page|
        contents = Nokogiri::XML(open(page))
    }
zip_archive(new_archive)

В этом примере на самом деле ничего не происходит, но только наличие Nokogiri::XML(open(page)) достаточно, чтобы вызвать ошибки. Это происходит для каждой страницы, которая открывается через Nokogiri. Поэтому, если я изменю его только на одну страницу:

contents = Nokogiri::XML(open(Dir.glob("temp/**/one_page.xhtml")))

, тогда FileUtils.rm_rf 'temp' успешно удалит файлы во временной папке , за исключением для one_page.xhtml, которая выдает ошибку «невозможно удалить».

Есть ли способ обойти эту проблему, так что я все еще могу использовать Nokogiri в моем Ruby сценарии, но при этом скрипт не считает, что «процесс» Nokogiri Все еще работает? Это относится к c до Windows, поскольку на Mac не было таких проблем.

1 Ответ

0 голосов
/ 19 апреля 2020

Глядя на код:

Dir.glob("temp/**/*.{html,xhtml}").each { |page|
        contents = Nokogiri::XML(open(page))
    }

проблема действительно в том, что вы используете все доступные файловые дескрипторы. Это вовсе не проблема Nokogiri, просто она возникла в городе, когда возникла проблема.

В операционных системах имеется пул файловых дескрипторов; Они не бесконечный ресурс. Если у вас есть огромное количество файлов, которые находят, перебирают их и оставляют открытыми, то вы потребляете их все, что является плохим программированием.

Использование блочной формы для File.open обойдёт проблему, но File.read без блока чище, короче и, на мой взгляд, намного лучше go.

Dir.glob("temp/**/*.{html,xhtml}").each { |page|
  contents = Nokogiri::XML(File.read(page))
  # do something with contents
}

Но использование Dir.glob также способствует этой и другой проблеме. Вы просите систему выполнить поиск на диске, чтобы найти все подходящие файлы, а затем вернуть их в виде массива в памяти, которые затем повторяются. Вместо этого я настоятельно рекомендую использовать Find, который находится в стандартной библиотеке Ruby. Он ведет себя намного лучше в такой ситуации.

Модуль Find поддерживает нисходящий обход набора путей к файлам.

Например, для суммирования размера всех файлов в вашем домашнем каталоге, игнорируя все в «точечном» каталоге (например, $ HOME / .s sh):

require 'find'

total_size = 0

Find.find(ENV["HOME"]) do |path|
  if FileTest.directory?(path)
    if File.basename(path).start_with?('.')
      Find.prune       # Don't look any further into this directory.
    else
      next
    end
  else
    total_size += FileTest.size(path)
  end
end

Используя Find, вы можете запустить код для огромного диска, содержащего миллионы совпадений, и он ' Вы будете работать лучше, чем Dir.glob.

. Настроив свой пример, этот непроверенный код должен помочь вам начать:

require 'find'
require 'nokogiri'

Find.find('temp') do |path|
  if FileTest.file?(path) && path[/\.x?html$/i]
    contents = Nokogiri::XML(File.read(page))
    # do something with contents
  end
end

Вторая проблема, с которой вы часто сталкиваетесь, используя Dir.glob чтобы выполнить поиск сверху вниз (**), он немедленно попросит ОС найти все подходящие файлы, а затем подождет, пока ОС соберет их. Если вместо этого вы используете Find, ваш код будет приостанавливаться для каждого поиска следующего совпадения в иерархии, но это будет гораздо более короткая пауза, в результате чего более отзывчивое приложение не будет потреблять столько памяти или работать быстрее. сбор файлов на диске. На удалённо смонтированном диске или файловом сервере вы можете раздражать системного администратора, когда он замечает огромные скачки сетевого и дискового ввода-вывода вместо незначительного увеличения активности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...