Как запустить команду оболочки и сделать вывод на печать во время выполнения команды? - PullRequest
0 голосов
/ 16 марта 2019

Я использую приведенную ниже функцию для запуска команд оболочки, но не могу заставить ее напечатать вывод во время выполнения команды. Например, если я запускаю / usr / sbin / system_profiler, мне нужно дождаться завершения выполнения команды, прежде чем я смогу увидеть вывод.

Как распечатать вывод команды оболочки, пока она еще выполняется?

func runCommand(cmd: String, args: String...) -> (output: [String], error: [String], exitCode: Int32) {
print("running shell command")
var output: [String] = []
var error: [String] = []
let task = Process()
task.launchPath = cmd
task.arguments = args
let outpipe = Pipe()
task.standardOutput = outpipe
let errpipe = Pipe()
task.standardError = errpipe
task.launch()
let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String(data: outdata, encoding: .utf8) {
    string = string.trimmingCharacters(in: .newlines)
    output = string.components(separatedBy: "\n")
}
let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String(data: errdata, encoding: .utf8) {
    string = string.trimmingCharacters(in: .newlines)
    error = string.components(separatedBy: "\n")
}
task.waitUntilExit()
let status = task.terminationStatus
print("shell end")
return (output, error, status)
}

Ответы [ 2 ]

0 голосов
/ 16 марта 2019

В частности, причина блокировки: outpipe.fileHandleForReading.readDataToEndOfFile()

. Вы можете попробовать следующее, чтобы проверить это: ...

     task.standardOutput = outpipe
     let errpipe = Pipe()
      task.standardError = errpipe

    task.launch()

       repeat{
    let outdata = outpipe.fileHandleForReading.readData(ofLength: 100) //.readDataToEndOfFile()
    if var string = String(data: outdata, encoding: .utf8) {
        string = string.trimmingCharacters(in: .newlines)
        output = string.components(separatedBy: "\n")
 print(output)
    }
    let errdata = errpipe.fileHandleForReading.readData(ofLength: 100)// .readDataToEndOfFile()
    if var string = String(data: errdata, encoding: .utf8) {
        string = string.trimmingCharacters(in: .newlines)
        error = string.components(separatedBy: "\n")
        }} while (task.isRunning)

или добавить readerHandler:

 outpipe.fileHandleForReading.readabilityHandler = { file in
         let  outdata = fileHandle.readData(ofLength: 100)
            if var string = String(data: outdata, encoding: .utf8) {
                string = string.trimmingCharacters(in: .newlines)
             print( string.components(separatedBy: "\n"))

        } 

Тогда у вас может быть много способов, чтобы избежать этого, нажмите на ссылку в Timer или Observer в других темах.

0 голосов
/ 16 марта 2019

Вы должны сделать свою задачу асинхронной и readInBackgroundAndNotify.

Что-то вроде этого (не проверено), он использует terminationHandler и добавляет наблюдателя readCompletionNotification для получения уведомлений.

var output = ""

func runCommand(cmd: String, args: String..., completion: @escaping (String, String, Int32) -> Void) {
    print("running shell command")
    let task = Process()
    task.launchPath = cmd
    task.arguments = args
    let outpipe = Pipe()
    task.standardOutput = outpipe
    let errpipe = Pipe()
    task.standardError = errpipe
    task.terminationHandler = { [unowned self] returnedTask in
        NotificationCenter.default.removeObserver(self,
                                                  name: FileHandle.readCompletionNotification,
                                                  object: (returnedTask.standardOutput as! Pipe).fileHandleForReading)
        let status = returnedTask.terminationStatus
        if status == 0 {
           completion(output, "", status)
        } else {
            let errorData = errpipe.fileHandleForReading.readDataToEndOfFile()
            let errorString = String(data:errorData, encoding: .utf8)!
            completion("", errorString, status)
        }
    }
    let outputHandle = (task.standardOutput as! Pipe).fileHandleForReading
    NotificationCenter.default.addObserver(forName: FileHandle.readCompletionNotification, object: outputHandle, queue: OperationQueue.current, using: { notification in
        if let data = notification.userInfo?[NSFileHandleNotificationDataItem] as? Data, !data.isEmpty {
            output.append(String(data: data, encoding: . utf8)!)
        } else {
            task.terminate()
            return
        }
        outputHandle.readInBackgroundAndNotify()
    })
    outputHandle.readInBackgroundAndNotify()
    task.launch()
}

Если вы хотите распечатать данные при получении, замените output.append(String(data: data, encoding: . utf8)!) на print(String(data: data, encoding: . utf8)!), и тогда вам, вероятно, также не понадобится обработчик завершения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...