Swift:FileManager判断可执行文件在Mac应用失效,单元测试正常
问题描述
我有一个已设置Unix可执行位的文件(路径为/Users/me/Documents/Projects/example.sh)。在Swift macOS应用中,需要判断该文件是否已设置可执行位。
使用FileManager.default.isExecutableFile(atPath:)在单元测试中可正常工作,但在运行的macOS应用中失效(同时尝试了try url.resourceValues(forKeys: [URLResourceKey.isExecutableKey]).isExecutable,结果一致)。
单元测试代码运行正常:
final class MyTests: XCTestCase { // -> ✅ func test_isExecutableFile_worksFromUnitTest() { let path = "/Users/me/Documents/Projects/example.sh" XCTAssertTrue(FileManager.default.isExecutableFile(atPath: path)) } }
而在应用内调用则无法正常工作:
class MyClass { // -> prints 🛑 func methodInAClassInMyMacOSApp() { let path = "/Users/me/Documents/Projects/example.sh" let isExec = FileManager.default.isExecutableFile(atPath: path) print(isExec ? "✅" : "🛑") } }
猜测这是macOS应用沙箱机制导致的,而单元测试不受该限制。仅需判断文件是否标记为可执行,无需实际执行它,请问在macOS 13及以上系统中如何用Swift实现?
解决方案
沙箱会限制FileManager的高层API返回结果(返回的是应用是否有执行权限,而非文件本身的权限位),要直接读取文件的Unix可执行标记,可采用以下两种方法:
方法1:调用底层stat系统函数
直接通过系统调用获取文件权限标志,绕过沙箱对高层API的限制:
import Foundation func isFileMarkedExecutable(at path: String) -> Bool { var fileStat = stat() guard stat(path, &fileStat) == 0 else { // 文件不可访问(无沙箱权限或文件不存在) return false } // 检查用户、组、其他角色的可执行位是否有任意一个被设置 let executableMask = S_IXUSR | S_IXGRP | S_IXOTH return (fileStat.st_mode & executableMask) != 0 }
使用示例:
class MyClass { func methodInAClassInMyMacOSApp() { let path = "/Users/me/Documents/Projects/example.sh" let isExec = isFileMarkedExecutable(at: path) print(isExec ? "✅" : "🛑") } }
方法2:使用posixPermissionsKey读取权限
macOS 13+支持通过URLResourceValues直接获取文件的POSIX权限,再解析可执行位:
import Foundation func isFileMarkedExecutable(at url: URL) -> Bool { do { let resourceValues = try url.resourceValues(forKeys: [.posixPermissionsKey]) guard let permissions = resourceValues.posixPermissions else { return false } // 0o111是Unix可执行位的八进制掩码 let executableMask = 0o111 return (permissions & executableMask) != 0 } catch { return false } }
使用示例:
class MyClass { func methodInAClassInMyMacOSApp() { let url = URL(filePath: "/Users/me/Documents/Projects/example.sh") let isExec = isFileMarkedExecutable(at: url) print(isExec ? "✅" : "🛑") } }
注意事项
- 无论哪种方法,应用必须拥有该文件的读取权限(比如通过文件选择器让用户授权,或在
Info.plist中配置对应文件夹的访问权限),否则会因沙箱限制调用失败。 - 两种方法均只判断文件本身的Unix权限标记,不涉及应用是否具备执行该文件的权限。
内容的提问来源于stack exchange,提问作者Grant Neufeld
相关产品推荐
相关产品推荐

