【问题标题】:How can I tell if a symbolic link exists at a certain path?如何判断某个路径是否存在符号链接?
【发布时间】:2019-01-24 20:23:56
【问题描述】:

我有一个原始文件 /path/to/foo.txt 和一个指向它的符号链接 /other/path/to/foo.txt。我删除了/path/to/foo.txt,但保留了符号链接。 如何使用 Cocoa API 判断符号链接仍然存在?


我通过使用标准/推荐的FileManager.fileExists(atPath:) 发现了这一点。对于不熟悉该 API 的任何人来说,这里的问题是它遍历符号链接。所以,当我这样做时:

FileManager.default.fileExists(atPath: "/other/path/to/foo.txt")

它返回false,因为它看到我给了它一个符号链接并解析它,然后看到解析路径上没有文件。

正如文档所说:

如果您的应用程序无法访问位于path 的文件,可能是因为无法访问一个或多个父目录,则此方法返回false。如果path 中的最后一个元素指定了符号链接,则此方法会遍历该链接并根据链接目标处文件的存在返回truefalse

FileManager 中似乎没有替代方案。所以,我想知道是否可以调用 Cocoa API 来判断那里是否存在符号链接,或者我是否必须求助于 C 或 Bash API。

【问题讨论】:

    标签: swift cocoa symlink nsfilemanager symlink-traversal


    【解决方案1】:

    您不需要/使用 FileManager。而且你不应该再为 anything 使用字符串文件路径了。

    以文件 URL 开头——"/other/path/to/foo.txt" 的 URL 版本。现在读取文件的.isSymbolicLink 资源键并查看它是否是符号链接。如果是,但如果指向的文件不存在,你就知道你的链接坏了。

    我在操场上写了一个小测试:

    let url = URL(fileURLWithPath: "/Users/mattneubelcap/Desktop/test.txt")
    if let ok = try? url.checkResourceIsReachable(), ok {
        let vals = url.resourceValues(forKeys: [.isSymbolicLinkKey])
        if let islink = vals.isSymbolicLink, islink {
            print("it's a symbolic link")
            let dest = url.resolvingSymlinksInPath()
            let report = dest != url ? "It exists" : "It doesn't exist"
            print(report)
        }
    }
    

    【讨论】:

    • 谢谢!我实际上正在使用一个瘦包装器扩展函数,它接受一个 URL 并传递它,但为了这个问题,我预先考虑了这些信息。
    • 添加了实际代码示例。找出链接是否坏了比我想象的要棘手。你要问解析的路径是否和链接路径一样。
    • FileManager 似乎知道它是否坏了,但这不是我现在关心的;我稍后会在程序执行时解决它。我只是想确保链接在那里,为将来的解决做好准备。
    【解决方案2】:

    替换 fileExists(atPath:) 的解决方案是使用 attributesOfItem(atPath:) 返回节点的类型(FileAttributeKey.type),如果文件/节点不存在,则抛出错误代码=260。

    所以这里是我的“解释”:

    func nodeExists(atPath path: String) -> FileType? {
    do {
        let attribs = try fm.attributesOfItem(atPath: path)
        if let type = attribs[FileAttributeKey.type] {
            switch (type as! FileAttributeType) {
                case FileAttributeType.typeDirectory: return .directory
                case FileAttributeType.typeRegular: return .file
                case FileAttributeType.typeSymbolicLink: return .symlink
                default:
                    return nil
                }
            }
        } catch {
            if (error as NSError).code == 260 {
                return false
            } else {
                return nil
            }
        }
    }
    

    挖掘错误代码的方法

    【讨论】:

    • 每个 Swift 错误都可以转换为 NSError,所以(error as NSError).code
    • 什么是 260?命名错误代码比数字更具有语义价值。
    • 随意编号或命名 :-)
    【解决方案3】:

    这里有一个更简单的方法:Fondation/FileManger/FileWrapper

    let node = try FileWrapper(url: URL(fileURLWithPath: "/PATH/file.link"), options: .immediate)
    
    node.isDirectory      >> false
    node.isRegularFile    >> false
    node.isSymbolicLink   >> true
    

    【讨论】:

    • 这种方法的一个问题是它可能非常昂贵,因为它遍历目录树。根据文档“如果 url 是一个目录,此方法会递归地为该目录中的每个节点创建文件包装器。使用 fileWrappers 属性获取目录包含的节点的文件包装器。”
    猜你喜欢
    • 1970-01-01
    • 2013-02-12
    • 1970-01-01
    • 2014-03-15
    • 2021-06-22
    • 2011-04-28
    • 2012-05-09
    • 2013-05-12
    • 1970-01-01
    相关资源
    最近更新 更多