簡體   English   中英

無法強制刪除目錄

[英]Unable to force the removal of a directory

我在 Windows 10 上的 Ruby 腳本中使用 Info-ZIP 實用程序來解壓縮存檔、編輯內容並重新壓縮它。 該腳本旨在遍歷一批檔案,並刪除提取內容時創建的臨時文件夾。 不過,該文件夾並未被刪除。 例如:

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

這在 Mac 上一直工作得很好(使用相同的腳本,結合 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. 不過,使用此文件的唯一“進程”是腳本本身。

一旦腳本運行完畢,如果我之后運行FileUtils.rm_rf "temp" ,它就會工作,並成功刪除目錄。 但是,我需要在每次迭代后並在同一個原始腳本中完成此操作,以便在執行結束時正確覆蓋和刪除目錄,而不會在命令提示符中出現任何錯誤或警告。

有沒有其他方法可以強行刪除這個文件夾?

更新:在對腳本的不同部分進行了大量測試后,我能夠找到問題的確切根源。 所以所有的檔案都包含 XHTML 文件。 該腳本在某些情況下需要復制存檔,並且復制的存檔的內容已修改。 是否需要復制取決於 XHTML 文件中是否存在某些標記。 該腳本使用 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'成功刪除了 temp 文件夾中的文件,但one_page.xhtml除外,這會引發“無法刪除”錯誤。

有沒有辦法繞過這個問題,這樣我仍然可以在我的 Ruby 腳本中使用 Nokogiri,但腳本不會認為 Nokogiri“進程”仍在運行? 這是 Windows 特有的,因為在 Mac 上沒有遇到此類問題。

查看代碼:

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

這個問題真的看起來像你正在消耗所有可用的文件句柄。 這根本不是 Nokogiri 的問題,問題發生時它恰好在城里。

操作系統有一個可用的文件句柄池; 它們不是取之不盡的資源。 如果你有大量的文件正在被發現,遍歷它們並讓它們保持打開狀態,那么你就在消耗它們,這是糟糕的編程。

使用File.read File.open清晰、更短,而且在我看來,這是一種比 go 更好的方法。

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

但是,使用Dir.glob也會導致這個問題和另一個問題。 您要求系統搜索磁盤以找到所有匹配的文件,然后將它們作為 memory 中的數組返回,然后對其進行迭代。 相反,我強烈建議使用 Ruby 標准庫中的Find 它在那種情況下表現得更好。

Find模塊支持自頂向下遍歷一組文件路徑。

例如,要計算主目錄下所有文件的總大小,忽略“點”目錄中的任何內容(例如 $HOME/.ssh):

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 ,您的代碼將在每次搜索層次結構中的下一個匹配項時暫停,但暫停時間會短得多,從而導致響應速度更快的應用程序不會吃掉那么多 memory 或擊敗磁盤收集文件。 在遠程安裝的驅動器或文件服務器上,當系統管理員注意到 huge.network 和磁盤 IO 出現峰值而不是活動略有增加時,您可能最終會激怒他們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM