Несколько терминальных команд, использующих NSTask одновременно

Мне нужно запустить следующую команду из приложения OSX:

доктор наук -читать / Пользователи / пользователь JPEGPhoto | хвост -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), что не работает.

Когда я попробовал их отдельно в терминале, это тоже не сработало.

Какие-либо предложения? Спасибо

ОБНОВИТЬ:

После того, как вы следовали великому совету Кена Томаса, вот как это выглядит в быстрой версии:

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)
}

1 ответ

Решение

Вы уверены, что вам нужно запустить эту команду и использовать 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 по команде. Вы создаете NSPipe для каждой трубы (|) в команде оболочки, которую вы эмулируете. Вы устанавливаете одну трубу как выход первой задачи и вход второй. Вы устанавливаете другой канал как выход второго и вход первого. Затем вы запускаете все три задачи и ждете, пока завершится последняя.

При желании вы можете использовать другой канал для стандартной ошибки каждой задачи и / или вывода последней задачи. Обязательно выполняйте асинхронное чтение из таких каналов одновременно с ожиданием завершения последней задачи (или, что еще лучше, не блокируйте ожидание последней задачи, а вернитесь к циклу обработки событий и дайте уведомлению задачи о ее завершении).

Аргументы для каждой задачи должны быть массивом. Если вы собираетесь попытаться выполнить эти задачи вместо использования надлежащего API, в идеале вы всегда должны иметь команду в виде массива, а не строки. Синтаксический анализ строки - это трудная задача, в зависимости от того, какие функции оболочки вы хотите эмулировать. Итак, для первой задачи путь запуска будет @"dscl" и аргументы будут @[ @".", @"-read", @"/Users/user", @"JPEGPhoto" ], Аналогично для других задач.

Просто для полноты я скажу, что вы можете запустить полную командную строку, например, исходную строку, задействовав оболочку для ее анализа и запуска подпроцессов для каждой из подкоманд. Установите путь запуска @"/bin/sh" и аргументы @[ @"-c", @"dscl . -read /Users/user JPEGPhoto | tail -1 | xxd -r -p > /Users/user/Desktop/user.jpg" ], Я действительно не рекомендую это, хотя. Предполагая, что настоящая команда, которую вы хотите выполнить, является динамической, и вы будете создавать командную строку программно, легко создавать проблемы. Если вы создадите строку, содержащую символы, которые обрабатывает оболочка специально, вы получите неожиданные результаты и, возможно, даже сделаете что-то опасное. Кроме того, лично мне кажется неправильным строить строку только для того, чтобы оболочка могла разорвать ее на части, когда вы могли бы выполнить оригинальную работу самостоятельно.

Другие вопросы по тегам