2

尝试从系统分析器中读取一些信息。为此,我使用 NSTask 运行一些终端行命令。如果我运行一些输出不太大的命令没有问题。(例如:SPInstallHistoryDataType)但是如果我运行“SPApplicationsDataType”命令来收集已安装的应用程序列表,NSTask 等待太多而没有任何结果和任何错误。

所以我开始认为应该有一个缓冲区大小或类似的东西,但我找不到任何关于它的东西。我不知道也许我走错路了。

func readData (dataType: String) -> NSArray? {
let out = NSPipe()
let task = NSTask()
task.launchPath = "/usr/sbin/system_profiler"
task.arguments = ["-xml",dataType]
task.standardOutput = out
task.launch()

task.waitUntilExit()

if task.terminationStatus != 0 {
    NSLog("system_profiler returned error status")
    return nil
}

let data = out.fileHandleForReading.readDataToEndOfFile()
let plist : AnyObject?
do {
    plist = try NSPropertyListSerialization.propertyListWithData(data,
        options: [.Immutable],
        format: nil)
} catch let error as NSError {
    NSLog("%@", "Failed to parse system_profiler results. \(error.localizedDescription)")
    return nil
}

return plist as? NSArray
}
let r = readData("SPInstallHistoryDataType")// There is no problem
let r2 = readData("SPApplicationsDataType") // Crash

注意:是的,我可以将此数据写入文件并从该文件中读取。但我试图了解问题所在。

4

2 回答 2

5

这绝对是缓冲问题。当您一次读取一个块时,它会起作用。

func getApplications() -> String?
{
    var retval=""
    let theTask = NSTask()
    let taskOutput = NSPipe()
    theTask.launchPath = "/usr/sbin/system_profiler"
    theTask.standardOutput = taskOutput
    theTask.standardError = taskOutput
    theTask.arguments = ["-xml", "SPApplicationsDataType"]
    theTask.launch()

    while (true) {
        let data = taskOutput.fileHandleForReading.readDataOfLength(1024)
        if (data.length <= 0) { break }
        let str = String(data: data, encoding: NSUTF8StringEncoding)!
        retval += str

        //print (str)
    }

    theTask.waitUntilExit()

    return retval
}
于 2016-03-14T00:36:12.510 回答
0

我在新的 Mac Pro 上也有类似的问题,但更糟。使用 macOS 10.15.3 Catalina,我无法获取“SPAudioDataType”的 system_profiler 数据。可以调用 curl 等其他进程,但 system_profiler 是一个问题。

我的问题非常有趣的是,这仅在重新启动后大约 10 分钟发生。在前 10 分钟内,无论是否使用处理程序,甚至使用上述答案中的代码“getApplications”,一切都正常工作。

是的,当然我在主线程中运行它,但是在主线程中运行与否没有区别。

我做了很多实验来看看,这种行为的根源是什么。我发现,我的程序在使用命令读取数据时挂起

let data = taskOutput.fileHandleForReading.readDataOfLength(1024)

在有错误数据可用的情况下,反之亦然,程序在使用命令读取错误消息时挂起

let data = taskError.fileHandleForReading.readDataOfLength(1024)

在有正常数据可用(但没有错误数据)的情况下。

如果我试图获取当前可用的数据量,程序甚至会挂起:

let c = taskError.fileHandleForReading.availableData.count

不管我先测试什么,如果没有可用数据,程序就会挂起。

所以我完全重写了我的函数来使用异步处理程序:

@discardableResult func launchprogram (_ launchpath: String, _ arguments: [String]) -> (result: String, error: Int)
{
    var out: String  = ""           // Output
    var err: String  = ""           // Error Messages
    var fin: Bool    = false        // If the process exits normally
    let pro: Process = Process()

    pro.arguments      = arguments
    pro.launchPath     = launchpath
    pro.standardOutput = Pipe()
    pro.standardError  = Pipe()
    let proOut: Pipe   = pro.standardOutput as! Pipe
    let proIn: Pipe    = pro.standardError  as! Pipe

    proOut.fileHandleForReading.readabilityHandler =
    {
        pipe in
        if let line = String(data: pipe.availableData, encoding: String.Encoding.utf8)
        {
            if line.count > 0 // Neuen Ausgabe-Text hinzufügen
            {
                out += line
            }
        }
    }

    proIn.fileHandleForReading.readabilityHandler =
    {
        pipe in
        if let line = String(data: pipe.availableData, encoding: String.Encoding.utf8)
        {
            if line.count > 0 // Neuen Fehler-Text hinzufügen
            {
                err += line
            }
        }
    }

    pro.terminationHandler =
    {
        (process) in
        fin = not(process.isRunning)
    }

    pro.launch()
    pro.waitUntilExit()

    if err == ""
    {
        if fin
        {
            return (out, 0)
        }
        else
        {
            return (out, -1)
        }
    }
    else if out == ""
    {
        let message: String = "Error while executing:" + char(13) + char(13)
        return (message + err, -2)
    }
    else
    {
        let message: String = char(13) + char(13) + "Error while executing:" + char(13) + char(13)
        return (out + message + err, -3)
    }
}

此函数与上一篇文章中的“getApplications”函数的根本区别在于,我使用“处理程序”来管理输出和错误消息流。这总是有效的。部署目标可以是 10.9 或更高版本。我没有使用 10.8 及更早版本对其进行测试。所以我的问题是,在 Catalina 中,在某些情况下,不再可能以“正常”同步顺序获取信息,而只能使用处理程序异步获取信息。如果我中断执行,我总是处于类似“libsystem_kernel.dylib read" withe the calling function "Foundation_NSReadFromFileDescriptorWithProgress”的状态。我很高兴知道这是 Catalina 的问题(使用新的 Mac Pro)还是 Apple 希望我们使用的产品发生了根本性的变化。

于 2020-03-12T10:16:44.913 回答