Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I tell if a symbolic link exists at a certain path?

I have an original file, /path/to/foo.txt, and a symbolic link to it, /other/path/to/foo.txt. I delete /path/to/foo.txt, but leave the symbolic link in-place. How can I tell that the symbolic link still exists using Cocoa APIs?


I found this by using the standard/recommended FileManager.fileExists(atPath:). The problem here, for anyone unfamiliar with that API, is that it traverses symlinks. So, when I do this:

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

it returns false, because it saw that I gave it a symlink and resolved it, and then saw that there is no file at the resolved path.

As the documentation says:

If the file at path is inaccessible to your app, perhaps because one or more parent directories are inaccessible, this method returns false. If the final element in path specifies a symbolic link, this method traverses the link and returns true or false based on the existence of the file at the link destination.

There doesn't seem to be an alternative in FileManager. So, I'm wondering if I can call a Cocoa API to tell if a symlink exists there, or if I'll have to resort to C or Bash APIs.

like image 437
Ky. Avatar asked Dec 08 '22 14:12

Ky.


2 Answers

You don't need/use FileManager for this. And you should not be using string file paths for anything any more.

Start with a file URL — the URL version of your "/other/path/to/foo.txt". Now read the file's .isSymbolicLink resource key and see whether it's a symbolic link. If it is, but if the file pointed to doesn't exist, you know your link has gone bad.

I wrote a little test in a 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)
    }
}
like image 183
matt Avatar answered Mar 24 '23 00:03

matt


Here's a simpler way: Fondation/FileManger/FileWrapper

let node = try FileWrapper(url: URL(fileURLWithPath: "/PATH/file.link"), options: .immediate)

node.isDirectory      >> false
node.isRegularFile    >> false
node.isSymbolicLink   >> true
like image 21
Luc-Olivier Avatar answered Mar 24 '23 00:03

Luc-Olivier