繁体   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