Ruby スクリプトが深くネストされたディレクトリを再帰的に作成する
概要
私は 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) })
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)
問題は copy2tmp メソッド、その定義または呼び出し方法にあるのではないかと思います。このメソッドはファイルを一時ディレクトリにコピーすることを目的としていますが、ディレクトリ作成の再帰ループが発生しているようです。この問題の原因となっている copy2tmp メソッドまたはその呼び出しの欠陥を特定できる人はいますか?この意図しない再帰を阻止するための洞察があれば、大変感謝いたします。
解決策
それで、少し掘り下げる必要がありましたが、問題の核心を見つけました。
この行を使用していますExceptions =ignore +ignore.map { |s| “./#{s}” } + Dir[’.*’] + Dir[’./.*’] は、すべてのドット ファイルだけでなく、一部の無視ディレクトリを除外しようとします。
Ruby < 3.1 では、Dir[’.*’] に現在のディレクトリ (‘.’) と親ディレクトリ (‘..’) が含まれていたため、このフィルタは効果的でした。
Ruby 3.1 以降、Dir[’.*’] には親ディレクトリ (‘..’) が含まれなくなりました。このコミットを参照
ただし、Dir.entries(‘.’) には親ディレクトリが含まれます。
これは、電話をかけるときを意味します
Dir.entries('.').delete_if { |f| exceptions.include?(f) }
親ディレクトリは存在し、ファイル配列から削除されないため、親ディレクトリをメソッドに渡します。このメソッドは再帰的であり、それ自体を呼び出すときに現在のディレクトリをファイルリストに追加するため、これを Ruby 3.1 以降で実行すると、終わりのないループに陥っていました。
この問題を解決する他の解決策はありますが、最も簡単で最も下位互換性のある方法は、Dir.children(‘.’) を使用することです。これは、Dir::children が ‘.’ を含まないことを保証しているためです。または ‘..’
実例