You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

macOS(Swift)下无需diskutil CLI包装器获取BSD驱动器名称

在macOS Swift开发中获取所有USB/内置磁盘BSD名称的最优方案(不含CLI包装器)

问题描述

需要在macOS平台用Swift开发,完全不依赖diskutil CLI包装器获取所有USB设备(包含Mac内置磁盘)的BSD名称列表,要求同时覆盖已挂载和已弹出的设备。当前实现的代码仅能返回已挂载设备,希望找到更高效可靠的方案(比如基于IOReg或Disk Arbitration),因为CLI交互方式速度慢且稳定性差。

期望返回结果示例:

["disk0", "disk0s1", "disk0s2", "disk0s3", "disk1", "disk1s1", "disk1s2", "disk1s3", "disk1s4", "disk2", "disk2s1", "disk2s2", "disk3", "disk3s1", "disk3s1s1", "disk3s2", "disk3s3", "disk3s4", "disk3s5", "disk3s6", "disk4", "disk4s1", "disk4s2", "disk5", "disk5s1", "disk5s2", "disk6", "disk6s1", "disk6s2", "disk10", "disk10s1", "disk10s2", "disk11", "disk11s1"]

当前仅能获取已挂载设备的代码:

static func getMountedBSDNames() -> [String] {
    guard let session = DASessionCreate(nil) else { return [] }
    guard let mountedVolumeURLs = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: nil) else { return [] }
    
    var BSDNames: [String] = []
    
    for volumeURL in mountedVolumeURLs {
        if let disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, volumeURL as CFURL), let BSDName = DADiskGetBSDName(disk) {
            BSDNames.append(
                String(cString: BSDName)
            )
        }
    }
    
    return BSDNames
}

最优实现方案

方案1:基于Disk Arbitration枚举所有磁盘(推荐)

Disk Arbitration框架不仅能处理已挂载卷,还能枚举系统中所有已连接的磁盘设备(包括弹出但未物理移除的),无需依赖CLI,性能稳定:

import DiskArbitration
import Foundation

static func getAllBSDNames() -> [String] {
    guard let session = DASessionCreate(nil) else { return [] }
    var bsdnNames: [String] = []
    
    // 设置回调捕获所有磁盘设备
    DASessionSetDispatchQueue(session, DispatchQueue.global())
    DARegisterDiskAppearedCallback(session, nil) { _, diskRef in
        guard let bsdName = DADiskGetBSDName(diskRef) else { return }
        let name = String(cString: bsdName)
        if !bsdnNames.contains(name) {
            bsdnNames.append(name)
        }
    }
    
    // 触发初始设备枚举
    DASessionRun(session, 0)
    DASessionStop(session)
    
    return bsdnNames
}

方案2:基于IORegistry底层查询

如果需要更深度的设备控制,可以直接查询IORegistry,获取系统中所有磁盘设备的BSD名称,包括部分Disk Arbitration未主动枚举的设备:

import IOKit
import Foundation

static func getAllBSDNamesFromIOReg() -> [String] {
    var bsdnNames: [String] = []
    
    // 获取磁盘设备的IOReg迭代器
    guard let iterator = IOServiceGetMatchingIterator(kIOMainPortDefault, IOServiceMatching(kIOMediaClass)) else {
        return []
    }
    defer { IOObjectRelease(iterator) }
    
    var ioObject: io_object_t = 0
    while IOIteratorNext(iterator, &ioObject) == KERN_SUCCESS {
        defer { IOObjectRelease(ioObject) }
        
        // 提取BSD名称属性
        if let bsdName = IORegistryEntryCreateCFProperty(ioObject, kIOBSDNameKey as CFString, kCFAllocatorDefault, 0)?.takeRetainedValue() as? String {
            bsdnNames.append(bsdName)
        }
    }
    
    return bsdnNames
}

方案对比

  • Disk Arbitration方案:符合系统框架设计,自动处理设备热插拔事件,代码简洁易维护,适合绝大多数场景。
  • IORegistry方案:更底层,能覆盖更多边缘设备,但需要手动管理内存和迭代逻辑,适合需要深度定制的场景。

额外扩展:筛选USB设备

如果需要仅保留USB设备,可以在枚举时添加属性判断:

  • Disk Arbitration:通过DADiskGetDescription(diskRef)获取设备属性,检查是否属于USB总线;
  • IORegistry:通过IORegistryEntrySearchCFProperty查找设备的USB父路径,判断设备类型。

内容的提问来源于stack exchange,提问作者TechUnRestricted

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 00:01:03