Swift 運行 shell 命令(可實時輸出Log)

//
//  CommandRunner.swift
//  zhengyh
//
//  Created by zhengyh on 2019/5/9.
//  Copyright © 2019 zhengyh. All rights reserved.
//

import Cocoa

class CommandRunner: NSObject {
    
    /** 同步執行
     *  command:    shell 命令內容
     *  returns:    命令行執行結果,積壓在一起返回
     *
     *  使用示例
        let (res, rev) = CommandRunner.sync(command: "ls -l")
        print(rev)
     */
    static func sync(command: String) -> (Int, String) {
        let utf8Command = "export LANG=en_US.UTF-8\n" + command
        return sync(shellPath: "/bin/bash", arguments: ["-c", utf8Command])
    }
    
    /** 同步執行
     *  shellPath:  命令行啓動路徑
     *  arguments:  命令行參數
     *  returns:    命令行執行結果,積壓在一起返回
     *
     *  使用示例
        let (res, rev) = CommandRunner.sync(shellPath: "/usr/sbin/system_profiler", arguments: ["SPHardwareDataType"])
        print(rev)
     */
    static func sync(shellPath: String, arguments: [String]? = nil) -> (Int, String) {
        let task = Process()
        let pipe = Pipe()
        
        var environment = ProcessInfo.processInfo.environment
        environment["PATH"] = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
        task.environment = environment
        
        if arguments != nil {
            task.arguments = arguments!
        }
        
        task.launchPath = shellPath
        task.standardOutput = pipe
        task.launch()
        
        let data = pipe.fileHandleForReading.readDataToEndOfFile()
        let output: String = String(data: data, encoding: String.Encoding.utf8)!
        
        task.waitUntilExit()
        pipe.fileHandleForReading.closeFile()
        
        return (Int(task.terminationStatus), output)
    }
    
    /** 異步執行
     *  command:    shell 命令內容
     *  output:     每行的輸出內容實時回調(主線程回調)
     *  terminate:  命令執行結束狀態(主線程回調)
     *
     *  使用示例
        CommandRunner.async(command: "ls -l",
                            output: {[weak self] (str) in
                                self?.appendLog(str: str)
                            },
                            terminate: {[weak self] (code) in
                                self?.appendLog(str: "end \(code)")
                            })
     */
    static func async(command: String,
                      output: ((String) -> Void)? = nil,
                      terminate: ((Int) -> Void)? = nil) {
        let utf8Command = "export LANG=en_US.UTF-8\n" + command
        async(shellPath: "/bin/bash", arguments: ["-c", utf8Command], output:output, terminate:terminate)
    }
    
    /** 異步執行
     *  shellPath:  命令行啓動路徑
     *  arguments:  命令行參數
     *  output:     每行的輸出內容實時回調(主線程回調)
     *  terminate:  命令執行結束狀態(主線程回調)
     *
     *  使用示例
        CommandRunner.async(shellPath: "/usr/sbin/system_profiler",
                            arguments: ["SPHardwareDataType"],
                            output: {[weak self] (str) in
                                self?.appendLog(str: str)
                            },
                            terminate: {[weak self] (code) in
                                self?.appendLog(str: "end \(code)")
                            })
     */
    static func async(shellPath: String,
                      arguments: [String]? = nil,
                      output: ((String) -> Void)? = nil,
                      terminate: ((Int) -> Void)? = nil) {
        DispatchQueue.global().async {
            let task = Process()
            let pipe = Pipe()
            let outHandle = pipe.fileHandleForReading
            
            var environment = ProcessInfo.processInfo.environment
            environment["PATH"] = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
            task.environment = environment
            
            if arguments != nil {
                task.arguments = arguments!
            }
            
            task.launchPath = shellPath
            task.standardOutput = pipe
            
            outHandle.waitForDataInBackgroundAndNotify()
            var obs1 : NSObjectProtocol!
            obs1 = NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable,
                                                          object: outHandle, queue: nil) {  notification -> Void in
                                                            let data = outHandle.availableData
                                                            if data.count > 0 {
                                                                if let str = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
                                                                    DispatchQueue.main.async {
                                                                        output?(str as String)
                                                                    }
                                                                }
                                                                outHandle.waitForDataInBackgroundAndNotify()
                                                            } else {
                                                                NotificationCenter.default.removeObserver(obs1)
                                                                pipe.fileHandleForReading.closeFile()
                                                            }
            }
            
            var obs2 : NSObjectProtocol!
            obs2 = NotificationCenter.default.addObserver(forName: Process.didTerminateNotification,
                                                          object: task, queue: nil) { notification -> Void in
                                                            DispatchQueue.main.async {
                                                                terminate?(Int(task.terminationStatus))
                                                            }
                                                            NotificationCenter.default.removeObserver(obs2)
            }
            
            task.launch()
            task.waitUntilExit()
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章