Get terminal output after quick command

I run some commands in the terminal using this code:

system("the command here") 

And after I want to know what is the result of executing this command, for example. if i run

 system("git status") 

I want to read the actual change information in my repo. Is there any way to make this quick?

+8
terminal swift
source share
2 answers

NSTask is a class for running another program as a subprocess. You can capture program output, error output, exit status, and more.

Expanding my response to the xcode 6 swift system () command , here is a simple utility function to run the 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("\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) } 

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 output, but not in error messages or exit code:

 let output = runCommand("/usr/bin/git", args: "status").output 

The output and output of the error is returned as an array of strings, one row for each row.

The first argument to runCommand() should be the full path to the executable, for example, "/usr/bin/git" . You can run the program using the shell (which is what system() does):

 let (output, error, status) = runCommand("/bin/sh", args: "-c", "git status") 

The advantage is that the git executable is automatically found through the default search path. The downside is that you must quote / escape arguments correctly if they contain spaces or other characters that have 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: "\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 return (output, error, status) } 
+20
source share

system spawns a new process, so you cannot capture it. The equivalent that gives you a way to do this will be popen , which you can use as follows:

 import Darwin let fp = popen("ping -c 4 localhost", "r") var buf = Array<CChar>(count: 128, repeatedValue: 0) while fgets(&buf, CInt(buf.count), fp) != nil, let str = String.fromCString(buf) { print(str) } fclose(fp) 

However, do not do it like this. Use NSTask as Martin describes .

edit: based on your request to run several commands in parallel, here is some probably unreasonable code:

 import Darwin let commands = [ "tail /etc/hosts", "ping -c 2 localhost", ] let fps = commands.map { popen($0, "r") } var buf = Array<CChar>(count: 128, repeatedValue: 0) let results: [String] = fps.map { fp in var result = "" while fgets(&buf, CInt(buf.count), fp) != nil, let str = String.fromCString(buf) { result += str } return result } fps.map { fclose($0) } println("\n\n----\n\n".join(map(zip(commands,results)) { "\($0):\n\($1)" })) 

(seriously, use NSTask )

+2
source share

All Articles