查找目录是否包含 URL,跟随符号链接

Find whether a directory contains a URL, following symlinks

我有两个目录如下:

因此文件 X 有两个可能的绝对 URL:/A/X/B/C/X。 (AB 可以在我的文件系统中的任何位置。)

我需要做的是,给定目录 B (file:///B/) 的文件 URL 和文件 URL 的任一文件X,判断文件X是否在目录B.

这是我想出的:

extension URL {

    func isIdenticalFile(to other: URL) -> Bool {
        return resolvingSymlinksInPath() == other.resolvingSymlinksInPath()
    }

    func contains(_ other: URL) -> Bool {
        guard isFileURL, other.isFileURL, let enumerator = FileManager.default.enumerator(atPath: path) else {
            return false
        }

        for subURL in enumerator.map({ appendingPathComponent([=11=] as! String) }) {
            if subURL.isIdenticalFile(to: other) || subURL.contains(other) {
                return true
            }
        }

        return false
    }

}

let b = URL(string: "file:///B/")!
let ax = URL(string: "file:///A/X")!
let bcx = URL(string: "file:///B/C/X")!

// Both b.contains(ax) and b.contains(bcx) are true

有simpler/more有效的方法吗?

确定两个 URL 是否引用相同内容的更好方法 文件是为了比较他们的fileResourceIdentifier。来自文档:

An identifier which can be used to compare two file system objects for equality using isEqual.

Two object identifiers are equal if they have the same file system path or if the paths are linked to same inode on the same file system. This identifier is not persistent across system restarts.

确定资源标识符应该比完全确定更快 解析文件路径。此外,这还会检测到硬链接 同一个文件。

更多备注:

  • 您的代码中的递归不是必需的,因为枚举数 已经进行了“深度”枚举。
  • 使用 enumerator(at: self, ...) 您可以获得 URL 的枚举器 而不是路径,这样您就不必构建 subURL.

代码可能如下所示:

extension URL {
    
    // Helper property get the resource identifier:
    private var identifier: NSObjectProtocol? {
        return (try? resourceValues(forKeys: [.fileResourceIdentifierKey]))?.fileResourceIdentifier
    }
    
    func contains(_ other: URL) -> Bool {
        guard isFileURL, other.isFileURL else {
            return false
        }
        guard let otherId = other.identifier else {
            return false
        }
        
        guard let enumerator = FileManager.default.enumerator(at: self, includingPropertiesForKeys: [.fileResourceIdentifierKey]) else {
            return false
        }
        
        for case let subURL as URL in enumerator {
            if let fileId = subURL.identifier, fileId.isEqual(otherId) {
                return true
            }
        }
        
        return false
    }
    
}