Swift 2.1 OSx shell commands using NSTask work whe

2019-02-27 11:05发布

I wrote a simple OSx (10.11) application to execute shell commands when a button is pressed. It works when I run it from xcode, but when I export the application via "archive", one of the buttons no longer works. I don't get an error and I don't get any output either. I am using absolute paths so I don't understand why it works in xcode but not as an exported application, Nor do I understand why one button works and the other doesn't.

Here is the main function that I am using the make the shell commands

    func runCommand(path : String, args : [String]) -> (output: [String], error: [String], exitCode: Int32) {
    var output : [String] = []
    var error : [String] = []

    let task = NSTask()
    task.launchPath = path
    task.arguments = args

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

    task.launch()

    let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
    if var string = String.fromCString(UnsafePointer(outdata.bytes)) {
        string = string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
        output = string.componentsSeparatedByString("\n")
    }

    let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
    if var string = String.fromCString(UnsafePointer(errdata.bytes)) {
        string = string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
        error = string.componentsSeparatedByString("\n")
    }

    //task.waitUntilExit()
    let status = task.terminationStatus

    return (output, error, status)
}

and here is the button that works:

      // Check for configurator 2 app installation
    let (output, error, status) = self.runCommand("/bin/bash", args:  ["-c", "/bin/ls", "/Applications/Apple Configurator 2.app"])

and here is the button that doesn't:

        // Check if the phone is plugged in and paired
    let (output, error, status) = self.runCommand("/bin/bash", args: ["-c", "/usr/local/bin/cfgutil", "get", "isPaired"])

What is even more strange, I discovered (through sheer frustration) that if I repeatedly click the button that doesn't work, it will sometimes eventually work.

2条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-02-27 11:24

Thanks to Eric D. I simplified my code and now everything is working.

func runCommand(path : String, args : [String]) -> (output: NSString, error: NSString, exitCode: Int32) {
    let task = NSTask()
    task.launchPath = path
    task.arguments = args

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

    task.launch()

    let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
    let output = NSString(data: outdata, encoding:  NSUTF8StringEncoding)

    let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
    let error_output = NSString(data: errdata, encoding: NSUTF8StringEncoding)

    task.waitUntilExit()
    let status = task.terminationStatus

    return (output!, error_output!, status)
}
查看更多
SAY GOODBYE
3楼-- · 2019-02-27 11:34

Your issue is the result of two things happening together:

  • you return default values

  • you don't specify alternative branches for the control flow

What happens is that it hides potential failures, and leads to code that is very hard to debug, as you experienced.

A possible solution with your existing code is to cover all possible ways, meaning providing else branches to your if var string = String.fromCString(UnsafePointer(errdata.bytes)) conditions, where you will handle errors.

查看更多
登录 后发表回答