如何运行shell命令并在命令运行时打印输出?

时间:2019-03-16 11:26:39

标签: swift shell

我使用下面的函数运行shell命令,但是在命令运行时似乎无法获取它来打印输出。例如,如果我运行/ usr / sbin / system_profiler,则必须等到命令执行完毕,才能看到输出。

当命令仍在运行时,如何打印shell命令的输出?

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 :(得分:0)

您必须使您的任务与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)!),然后可能也不需要完成处理程序。

答案 1 :(得分:0)

具体来说,阻止原因为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中点击链接。