Obter saída de terminal após um comando rápido

Eu corro alguns comandos no terminal com este código:

system("the command here") 

E depois eu quero saber qual é o resultado da execução deste comando, por exemplo, se eu corro

 system("git status") 

Eu quero ler as informações reais sobre as alterações no meu repository. Existe alguma maneira de fazer isso rapidamente?

NSTask é class para executar outro programa como um subprocess. Você pode capturar a saída do programa, saída de erro, status de saída e muito mais.

Expandindo minha resposta para o comando xcode 6 swift system () , aqui está uma function de utilitário simples para executar um comando de forma síncrona e retornar a saída, saída de erro e código de saída (agora atualizado para o 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) } 

Uso da amostra:

 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) } 

Ou, se você estiver interessado apenas na saída, mas não nas mensagens de erro ou no código de saída:

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

A saída e a saída de erro são retornadas como uma matriz de cadeias, uma cadeia para cada linha.

O primeiro argumento para runCommand() deve ser o caminho completo para um executável, como "/usr/bin/git" . Você pode iniciar o programa usando um shell (que é o que o system() também faz):

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

A vantagem é que o executável “git” é encontrado automaticamente por meio do caminho de pesquisa padrão. A desvantagem é que você deve citar / escaping argumentos corretamente se eles contiverem espaços ou outros caracteres que tenham um significado especial no shell.


Atualização para o 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) } 

system gera um novo processo para que você não possa capturar sua saída. O equivalente que lhe dá uma maneira de fazer isso seria popen , que você poderia usar assim:

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

No entanto, não faça dessa maneira. Use NSTask como Martin descreve .

edit: com base no seu pedido para executar vários comandos em paralelo, aqui está um código provavelmente imprudente:

 import Darwin let commands = [ "tail /etc/hosts", "ping -c 2 localhost", ] let fps = commands.map { popen($0, "r") } var buf = Array(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)" })) 

(sério, use o NSTask )

Intereting Posts