同时使用NSTask的多个终端命令



我需要从OSX应用程序运行以下命令:

dscl-read/Users/user JPEGPhoto|tail-1|xxd-r-p>/Users/user/Desktop/user.jpg

我试过几种方法,比如:

func runScript(launchPath:String, scriptName:String) {
    let task = NSTask()
    task.launchPath = launchPath
    task.arguments = NSArray(objects: scriptName)
    let pipe = NSPipe()
    task.standardOutput = pipe
    task.launch()
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output: String = NSString(data: data, encoding: NSUTF8StringEncoding)
}

func runCommand(command: String) -> (output: String, exitStatus: Int) {
    let tokens = command.componentsSeparatedByString(" ")
    let launchPath = tokens[0]
    let arguments = tokens[1..<tokens.count]
    let task = NSTask()
    task.launchPath = launchPath
    task.arguments = Array(arguments)
    let stdout = NSPipe()
    task.standardOutput = stdout
    task.launch()
    task.waitUntilExit()
    let outData = stdout.fileHandleForReading.readDataToEndOfFile()
    let outStr = NSString(data: outData, encoding: NSUTF8StringEncoding)
    return (outStr, Int(task.terminationStatus))
}

问题是这些方法每次调用执行一个命令,所以我必须调用它们三次(dscl/tail/xxd),这不起作用。

当我在终端分别试用它们时,它也不起作用。

有什么建议吗?感谢

更新:

在遵循Ken Thomases的伟大建议后,这就是swift中的样子:

import Collaboration
func saveUserPicture() {
    var userImage:NSImage = CBUserIdentity(posixUID: getuid(), authority: CBIdentityAuthority.defaultIdentityAuthority()).image() as NSImage
    var userImageData:NSData = NSBitmapImageRep.representationOfImageRepsInArray(userImage.representations, usingType: NSBitmapImageFileType.NSJPEGFileType, properties: nil )
    userImageData.writeToFile("/Users/user/Desktop/file.jpg", atomically: true)
}

您确定需要运行该命令并使用NSTask吗?这些信息应该通过直接API提供。

特别是,我认为以下内容应该为用户提供图像:

NSImage* image = [[CBUserIdentity identityWithName:@"user" authority:[CBIdentityAuthority defaultIdentityAuthority]] image];

如果你真的想的话,你可以把它保存为一个文件:

NSData* data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType:NSJPEGFileType properties:nil];
[data writeToURL:someURL atomically:YES];

根据您实际提出的问题,每个命令需要使用一个NSTask。在要模拟的shell命令中,为每个管道(|)创建一个NSPipe。将一个管道设置为第一个任务的输出和第二个任务的输入。将另一个管道设置为第二个管道的输出和第一个管道的输入。然后运行所有三个任务,等待最后一个任务完成。

如果需要,可以为每个任务的标准错误和/或上一个任务的输出使用另一个管道。请确保在等待最后一个任务退出的同时异步读取这些管道(或者,更好的是,不要阻止等待最后一项任务,而是返回事件循环,让任务在完成时通知您)。

每个任务的参数都应该是一个数组。如果您要尝试运行这些任务,而不是使用正确的API,那么理想情况下,命令总是作为数组而不是字符串。解析字符串是一件很难做到的事情,这取决于你想模仿shell的哪些功能。因此,对于第一个任务,启动路径将为@"dscl",参数将为@[ @".", @"-read", @"/Users/user", @"JPEGPhoto" ]。其他任务也是如此。

为了完整起见,我要说的是,您可以像原始字符串一样运行完整的命令行,方法是登记shell来解析它,并为每个子命令运行子流程。将启动路径设置为@"/bin/sh",将参数设置为@[ @"-c", @"dscl . -read /Users/user JPEGPhoto | tail -1 | xxd -r -p > /Users/user/Desktop/user.jpg" ]。不过,我真的不建议这样做。假设您要运行的实际命令是动态的,并且您将以编程方式构建命令行,那么很容易产生问题。如果您构建一个包含shell特殊处理的字符的字符串,您将得到意外的结果,甚至可能做一些危险的事情。此外,就个人而言,当你可以直接自己做原始工作时,仅仅为了让外壳能够把它拆开而构建一个字符串是错误的。

最新更新