如何判断在某个路径下是否存在符号链接?

6
我有一个原始文件,路径是/path/to/foo.txt,还有一个符号链接/other/path/to/foo.txt指向它。我删除了/path/to/foo.txt,但保留了符号链接。如何使用Cocoa APIs判断符号链接是否仍然存在?
我通过使用标准/推荐的FileManager.fileExists(atPath:)方法找到了答案。对于不熟悉该API的人来说,问题在于它会遍历符号链接。因此,当我执行以下操作时:
FileManager.default.fileExists(atPath: "/other/path/to/foo.txt")

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

正如文档所说:

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

FileManager 中似乎没有其他选择。 因此,我想知道是否可以调用 Cocoa API 来判断符号链接是否存在,否则我将不得不使用 C 或 Bash API。

3个回答

11
您无需使用FileManager。而且您不应再使用字符串文件路径进行任何操作。
请从文件URL开始,即“/other/path/to/foo.txt”的URL版本。现在读取文件的.isSymbolicLink资源键并查看它是否为符号链接。如果是,但所指向的文件不存在,则说明该链接已经失效。
我在playground中编写了一个小测试:
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并传递它,但出于这个问题的考虑,我放弃了这些信息。 - Ky -
1
添加了实际的代码示例。发现链接是否失效比我想象的要棘手;必须询问解析路径是否与链接路径相同。 - matt
看起来 FileManager 会找出它是否出了问题,但这并不是我目前关心的;我将在程序执行后解决它。我只想确保链接存在,以备将来解决。 - Ky -

3
这里有一个更简单的方法:Fondation/FileManager/FileWrapper。
let node = try FileWrapper(url: URL(fileURLWithPath: "/PATH/file.link"), options: .immediate)

node.isDirectory      >> false
node.isRegularFile    >> false
node.isSymbolicLink   >> true

这种方法的问题在于它可能会因为遍历目录树而变得代价高昂。根据文档所述:“如果url是一个目录,则此方法会递归地为该目录中的每个节点创建文件包装器。使用fileWrappers属性获取包含在目录中的节点的文件包装器。” - Zsolt Szatmari

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
        }
    }
}

挖掘错误代码的方法 <<< 感谢Ben Leggiero提供的技巧 :-)

每个 Swift 错误都可以转换为 NSError,所以 (error as NSError).code - Ky -
2
260是什么?为错误代码命名比使用数字具有更多的语义价值。 - uchuugaka
数字或者名称都可以:-) - Luc-Olivier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接