命令后获取终端输出 swift

Get terminal output after a command swift

我 运行 使用此代码在终端中执行一些命令:

system("the command here")

然后我想知道 运行ning 这个命令的结果是什么,例如如果我 运行

system("git status")

我想阅读有关我的存储库更改的实际信息。在 swift 中有什么方法可以做到这一点吗?

system 生成一个新进程,因此您无法捕获其输出。为您提供执行此操作的等效方法是 popen,您可以像这样使用它:

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)

但是,不要这样做。使用 NSTask 作为 Martin describes.

编辑:根据您对 运行 多个并行命令的请求,这里有一些可能不明智的代码:

import Darwin

let commands = [
    "tail /etc/hosts",
    "ping -c 2 localhost",
]

let fps = commands.map { popen([=11=], "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([=11=]) }

println("\n\n----\n\n".join(map(zip(commands,results)) { "\([=11=]):\n\()" }))

(说真的,用NSTask

NSTask 是 class 到 运行 另一个程序作为子进程。你可以 捕获程序的输出、错误输出、退出状态等等。

扩展我对 xcode 6 swift system() command 的回答, 这是一个简单的实用函数,用于同步 运行 一个命令, 和 return 输出、错误输出和退出代码(现在更新为 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)
}

示例用法:

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

或者,如果您只对输出感兴趣,而不对 错误消息或退出代码:

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

输出和错误输出被return编辑为字符串数组,一个 每行的字符串。

runCommand() 的第一个参数必须是 可执行文件,例如 "/usr/bin/git"。您可以使用 shell 启动程序(system() 也是如此):

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

优点是自动找到"git"可执行文件 通过默认搜索路径。缺点是你必须 quote/escape 如果参数包含空格或其他参数则正确 在 shell.

中具有特殊含义的字符

Swift3 的更新:

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

我的 2 美分 swift 5.x,macOS 带回调,完成后调用。

final func doTaskFor(cmd: String, arguments: [String], callback: CallBackWithStr = nil){


let task = Process()

let absolutePath = <add your specific path..> 
let fullCmd = absolutePath+cmd

#if DEBUG
// used to debug.
let debugstr :String = fullCmd + " " + arguments.oneLine()
print(debugstr)

#endif

task.executableURL = URL(fileURLWithPath: fullCmd)
task.arguments = arguments

// Create 2 Pipes and make the task
let outPipe = Pipe()
task.standardOutput = outPipe

let errPipe = Pipe()
task.standardError = errPipe

task.terminationHandler = { (process) in
    
    print("\ndidFinish: \(!process.isRunning)")
    
    // Get the data
    let outData = outPipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: outData, encoding: .utf8)
    // print(output!)
    
    // Get the error
    let errData = errPipe.fileHandleForReading.readDataToEndOfFile()
    let err = String(data: errData, encoding: .utf8)
    // print(err!)
    
    // usually output is empty if error.
    
    callback?(output ?? "")
    
}

do {
    try task.run()
} catch {
    let msg = " \(error)"
    Log(msg: msg, safe: true)
    print(msg)
    
}

}