如何在swift脚本中运行terminal命令? (例如xcodebuild)

我想用swiftreplace我的bash脚本。 我不知道如何调用普通的terminal命令,如lsxcodebuild

 #!/usr/bin/env xcrun swift import Foundation // Works println("Test") // Works ls // Fails xcodebuild -workspace myApp.xcworkspace // Fails 

 $ ./script.swift ./script.swift:5:1: error: use of unresolved identifier 'ls' ls // Fails ^ ... etc .... 

如果你在Swift代码中不使用命令输出,那么下面就足够了:

 #!/usr/bin/env swift import Foundation @discardableResult func shell(_ args: String...) -> Int32 { let task = Process() task.launchPath = "/usr/bin/env" task.arguments = args task.launch() task.waitUntilExit() return task.terminationStatus } shell("ls") shell("xcodebuild", "-workspace", "myApp.xcworkspace") 

更新:对于Swift3 / Xcode8

这里的问题是,你不能混合和匹配Bash和Swift。 您已经知道如何从命令行运行Swift脚本,现在您需要添加在Swift中执行Shell命令的方法。 总结来自PracticalSwift博客:

 func shell(launchPath: String, arguments: [AnyObject]) -> String { let task = NSTask() task.launchPath = launchPath task.arguments = arguments let pipe = NSPipe() task.standardOutput = pipe task.launch() let data = pipe.fileHandleForReading.readDataToEndOfFile() let output: String = NSString(data: data, encoding: NSUTF8StringEncoding)! return output } 

下面的Swift代码将执行带参数的xcodebuild ,然后输出结果。

 shell("xcodebuild", ["-workspace", "myApp.xcworkspace"]); 

至于search目录内容(这是什么ls在Bash中),我build议使用NSFileManager和直接扫描目录在Swift中,而不是Bash输出,这可能是一个痛苦的parsing。

效用函数在Swift 3.0中

这也返回任务终止状态并等待完成。

 func shell(launchPath: String, arguments: [String] = []) -> (String? , Int32) { let task = Process() task.launchPath = launchPath task.arguments = arguments let pipe = Pipe() task.standardOutput = pipe task.standardError = pipe task.launch() let data = pipe.fileHandleForReading.readDataToEndOfFile() let output = String(data: data, encoding: .utf8) task.waitUntilExit() return (output, task.terminationStatus) } 

如果您想使用bash环境来调用命令,请使用以下bash函数,该函数使用固定版本的Legoless。 我不得不从shell函数的结果中删除一个尾随的换行符。

Swift 3.0: (Xcode8)

 import Foundation func shell(launchPath: String, arguments: [String]) -> String { let task = Process() task.launchPath = launchPath task.arguments = arguments let pipe = Pipe() task.standardOutput = pipe task.launch() let data = pipe.fileHandleForReading.readDataToEndOfFile() let output = String(data: data, encoding: String.Encoding.utf8)! if output.characters.count > 0 { //remove newline character. let lastIndex = output.index(before: output.endIndex) return output[output.startIndex ..< lastIndex] } return output } func bash(command: String, arguments: [String]) -> String { let whichPathForCommand = shell(launchPath: "/bin/bash", arguments: [ "-l", "-c", "which \(command)" ]) return shell(launchPath: whichPathForCommand, arguments: arguments) } 

例如,要获取当前工作目录的当前工作git分支:

 let currentBranch = bash("git", arguments: ["describe", "--contains", "--all", "HEAD"]) print("current branch:\(currentBranch)") 

基于Legoless的答案的完整脚本

 #!/usr/bin/env xcrun swift import Foundation func printShell(launchPath: String, arguments: [AnyObject] = []) { let output = shell(launchPath, arguments:arguments) if (output != nil) { println(output!) } } func shell(launchPath: String, arguments: [AnyObject] = []) -> String? { let task = NSTask() task.launchPath = launchPath task.arguments = arguments let pipe = NSPipe() task.standardOutput = pipe task.launch() let data = pipe.fileHandleForReading.readDataToEndOfFile() let output: String? = NSString(data: data, encoding: NSUTF8StringEncoding) return output } // > ls // > ls -a -g printShell("/bin/ls") printShell("/bin/ls", arguments:["-a", "-g"]) 

混合rintaro和Legoless的Swift 3的答案

 @discardableResult func shell(_ args: String...) -> String { let task = Process() task.launchPath = "/usr/bin/env" task.arguments = args let pipe = Pipe() task.standardOutput = pipe task.launch() task.waitUntilExit() let data = pipe.fileHandleForReading.readDataToEndOfFile() guard let output: String = String(data: data, encoding: .utf8) else { return "" } return output }