Ruby 脚本递归创建深度嵌套目录

问题描述 投票:0回答:1

我正在开发 一个 Ruby 脚本,特别是一个名为

copy2tmp
的方法,(复制粘贴的方法定义)

define_singleton_method(:copy2tmp) do |files|
  files.each do |f|
    if File.symlink?(f)
      # Avoid trouble with symlink loops

      # Delete old symlink if there is one, because:
      # If a proper file or directory has been replaced with a symlink,
      # remove the obsolete stuff.
      # If there already is a symlink, delete because it might have been
      # relinked.
      if File.exist?("#{PARAMS[:tmpdir]}/#{f}")
        FileUtils.rm("#{PARAMS[:tmpdir]}/#{f}")
      end

      # Create new symlink instead of copying
      File.symlink("#{PARAMS[:jobpath]}/#{f}", "#{PARAMS[:tmpdir]}/#{f}")
    elsif File.directory?(f)
      FileUtils.mkdir_p("#{PARAMS[:tmpdir]}/#{f}")
      copy2tmp(Dir.entries(f)\
                  .delete_if do |s| ['.', '..', ]\
                  .include?(s) end.map { |s| "#{f}/#{s}" })
      # TODO: Is this necessary? Why not just copy? (For now, safer and more adaptable.)
    else
      FileUtils.cp(f,"#{PARAMS[:tmpdir]}/#{f}")
    end
  end
end

这似乎创建了过度深层的嵌套目录,从而导致错误。该脚本旨在使用

ltx2any
处理 LaTeX 文档。当脚本尝试将文件复制到临时目录时,就会出现问题,但最终会创建一个像
../folderName/folderName/folderName/...
这样的深层嵌套结构。

copy2tmp(Dir.entries('.').delete_if { |f| exceptions.include?(f) })

我尝试过的:

  1. 使用递归深度进行调试:添加了调试语句以跟踪
    copy2tmp
    中的递归深度。这表明,每次递归调用,深度都会增加,目录路径也会变长 (
    ../folderName/...
    )。
define_singleton_method(:copy2tmp) do |files, depth = 0|
    puts "Debug: Recursion depth #{depth}, files: #{files.inspect}"
# ...
copy2tmp(Dir.entries(f).delete_if do |s| ['.', '..', ].include?(s) end.map { |s| "#{f}/#{s}" }, depth + 1)
# ...
copy2tmp(Dir.entries('.').delete_if { |f| exceptions.include?(f) }, 0)
  1. 手动检查:检查可能导致递归循环的符号链接或异常目录结构。这里没有发现任何明显的问题。我尝试在名称中带有或不带有空格的文件夹中运行脚本。

  2. 检查

    copy2tmp
    调用: 确保使用适当的参数调用
    copy2tmp
    并检查其递归调用。

  3. 期望与现实:我希望脚本将文件复制到临时目录,而不创建不必要的嵌套结构。相反,该脚本创建了一个深度嵌套的目录路径,导致“没有这样的文件或目录”错误。

问题:

我怀疑问题在于

copy2tmp
方法,无论是它的定义还是它的调用方式。该方法旨在将文件复制到临时目录中,但它似乎会导致目录创建的递归循环。任何人都可以帮助识别导致此问题的
copy2tmp
方法或其调用中的缺陷吗?任何有关停止这种意外递归的见解将不胜感激。

ruby windows recursion file-handling directory-structure
1个回答
0
投票

所以需要一些挖掘,但我找到了问题的症结。

您正在使用此行

exceptions = ignore + ignore.map { |s| "./#{s}" } + Dir['.*'] + Dir['./.*']
尝试过滤掉一些
ignore
目录以及所有点文件。

在 Ruby 中,< 3.1

Dir['.*']
包括当前目录 (
'.'
) 和父目录 (
'..'
),因此此过滤器有效。

从 Ruby 3.1 开始

Dir['.*']
不再包含父目录 (
'..'
)。看到这个提交

但是

Dir.entries('.')
确实包含父目录。

这意味着当你打电话时

Dir.entries('.').delete_if { |f| exceptions.include?(f) }

父目录将存在,并且不会从

files
数组中删除,因此您将父目录传递给该方法。由于此方法是递归的,并且在调用自身时会将当前目录附加到文件列表中,因此在 ruby 3.1+ 上运行此方法时,您会陷入永无止境的循环中。

虽然还有其他解决方案可以解决此问题,但最简单且向后兼容的方法是使用

Dir.children('.')
,因为
Dir::children
保证它不会包含
'.'
'..'

返回一个包含除“.”之外的所有文件名的数组。和给定目录中的“..”。

© www.soinside.com 2019 - 2024. All rights reserved.