我使用以下代码在终端中运行一些命令:
system("the command here")
在我想知道运行此命令的结果后,例如如果我跑
system("git status")
我想阅读有关我的回购更改的实际信息。有没有办法在swift中做到这一点?
答案 0 :(得分:26)
NSTask
是将另一个程序作为子进程运行的类。您可以
捕获程序的输出,错误输出,退出状态等等。
扩展我对xcode 6 swift system() command的回答, 这是一个简单的实用程序函数,用于同步运行命令, 并返回输出,错误输出和退出代码(现在更新为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
输出和错误输出作为字符串数组返回,一个 每行的字符串。
runCommand()
的第一个参数必须是a的完整路径
可执行文件,例如"/usr/bin/git"
。你可以使用shell启动程序(system()
也是这样做的):
let (output, error, status) = runCommand("/bin/sh", args: "-c", "git status")
优点是自动找到“git”可执行文件 通过默认搜索路径。缺点是你必须这样做 如果它们包含空格或其他,则正确引用/转义参数 在shell中具有特殊含义的字符。
更新 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)
}
答案 1 :(得分:2)
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($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)" }))
(严重的是,使用NSTask
)
答案 2 :(得分:0)
我的 swift 5.x 2 美分,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)
}
}