NSTask
is class to run another program as a subprocess. You can
capture the program's output, error output, exit status and much more.
Expanding on my answer to xcode 6 swift system() command,
here is a simple utility function to run a command synchronously,
and return the output, error output and exit code (now updated for Swift 2):
func runCommand(cmd : String, args : String...) -> (output: [String], error: [String], exitCode: Int32) {
var output : [String] = []
var error : [String] = []
let task = NSTask()
task.launchPath = cmd
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("
")
}
let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String.fromCString(UnsafePointer(errdata.bytes)) {
string = string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
error = string.componentsSeparatedByString("
")
}
task.waitUntilExit()
let status = task.terminationStatus
return (output, error, status)
}
Sample usage:
let (output, error, status) = runCommand("/usr/bin/git", args: "status")
print("program exited with status (status)")
if output.count > 0 {
print("program output:")
print(output)
}
if error.count > 0 {
print("error output:")
print(error)
}
Or, if you are only interested in the output, but not in
the error messages or exit code:
let output = runCommand("/usr/bin/git", args: "status").output
Output and error output are returned as an array of strings, one
string for each line.
The first argument to runCommand()
must be the full path to an
executable, such as "/usr/bin/git"
. You can start the program using a shell (which is what system()
also does):
let (output, error, status) = runCommand("/bin/sh", args: "-c", "git status")
The advantage is that the "git" executable is automatically found
via the default search path. The disadvantage is that you have to
quote/escape arguments correctly if they contain spaces or other
characters which have a special meaning in the shell.
Update for Swift 3:
func runCommand(cmd : String, args : String...) -> (output: [String], error: [String], exitCode: Int32) {
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: "
")
}
let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String(data: errdata, encoding: .utf8) {
string = string.trimmingCharacters(in: .newlines)
error = string.components(separatedBy: "
")
}
task.waitUntilExit()
let status = task.terminationStatus
return (output, error, status)
}