打开(FileManager.default.fileSystemRepresentation(withPath:路径),O_EVTONLY)returns -1

open(FileManager.default.fileSystemRepresentation(withPath: path), O_EVTONLY) returns -1

我正在使用 SKQueue 来监控 mac 文件系统中的一些文件夹。根据文档,我已将目录路径添加到队列中,但我注意到在添加路径时,SKQueue 中的以下代码行返回 -1,因此无法监视我的文件夹。

这是SKQueue Documentation.

以下是文档中的代码,写在控制器中class。

import SKQueue

class SomeClass: SKQueueDelegate {
  func receivedNotification(_ notification: SKQueueNotification, path: String, queue: SKQueue) {
    print("\(notification.toStrings().map { [=13=].rawValue }) @ \(path)")
  }
}

let delegate = SomeClass()
let queue = SKQueue(delegate: delegate)!

queue.addPath("/Users/steve/Documents")
queue.addPath("/Users/steve/Documents/dog.jpg")

以下是SKQueue依赖中的代码。

  public func addPath(_ path: String, notifyingAbout notification: SKQueueNotification = SKQueueNotification.Default) {
    var fileDescriptor: Int32! = watchedPaths[path]
    if fileDescriptor == nil {
      fileDescriptor = open(FileManager.default.fileSystemRepresentation(withPath: path), O_EVTONLY)
      guard fileDescriptor >= 0 else { return }
      watchedPaths[path] = fileDescriptor
    }

fileDescriptor = open(FileManager.default.fileSystemRepresentation(withPath: path), O_EVTONLY)

以上代码返回 -1,因此失败。

调用 open() 失败,可能是由于权限不足。自 macOS 10.15 起,应用无法在未经许可的情况下访问某些文件和文件夹(例如,用户的主目录)。 Read more here.

我得到了相同的 -1 return 代码,但不明白为什么。在寻找解决方案时,我在 https://github.com/MartinJNash/SwiftFolderMonitor 偶然发现了 SwiftFolderMonitor。 class 有效,所以我知道这不是权限问题。

SwiftFolderMonitor 使用 DispatchSource.makeFileSystemObjectSource 而不是 kevent,但它也采用 URL 参数而不是字符串路径。我修改了 SKQueue 以使用 URL 而不是 String 并且它有效。

这是我修改后的 addPath:

public func addPath(url: URL, notifyingAbout notification: SKQueueNotification = SKQueueNotification.Default) {
    let path = url.absoluteString
    var fileDescriptor: Int32! = watchedPaths[path]
    if fileDescriptor == nil {
        fileDescriptor = open((url as NSURL).fileSystemRepresentation, O_EVTONLY)
        guard fileDescriptor >= 0 else { return }
        watchedPaths[path] = fileDescriptor
    }

    var edit = kevent(
        ident: UInt(fileDescriptor),
        filter: Int16(EVFILT_VNODE),
        flags: UInt16(EV_ADD | EV_CLEAR),
        fflags: notification.rawValue,
        data: 0,
        udata: nil
    )
    kevent(kqueueId, &edit, 1, nil, 0, nil)

    if !keepWatcherThreadRunning {
        keepWatcherThreadRunning = true
        DispatchQueue.global().async(execute: watcherThread)
    }
}

我不知道为什么会这样,也许其他人可以对此有所启发。

我仍在尝试这两种解决方案,但看起来 SwiftFolderMonitor 可以满足我的所有需求(我只需要知道特定文件何时更改),而且它的代码简洁明了,所以我想我会使用它通过 SKQueue.

希望对您有所帮助。