尝试从系统探查器中读取一些信息。为此,我使用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
注意:是的我可以将此数据写入文件并从该文件中读取。但我试着了解问题所在。
答案 0 :(得分:3)
这绝对是一个缓冲问题。当你一次阅读一个块时,它就可以工作。
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
}
答案 1 :(得分: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”函数之间的根本区别在于,我使用“ handler”来管理输出和错误消息流。这始终有效。部署目标可以是10.9或更高版本。我没有在10.8和更早版本中对其进行测试。所以我的问题是,在某些情况下,在Catalina中不再可能以“正常”同步顺序获取信息,而只能使用处理程序进行异步处理。如果我中断执行,则总是处于“ libsystem_kernel.dylib read" withe the calling function "Foundation
_ NSReadFromFileDescriptorWithProgress”之类的位置。我很高兴知道这是Catalina问题(使用新的Mac Pro)还是Apple希望我们使用的根本改变。