Rob*_*ert 64 bash shell xcodebuild swift
我想用swift替换我的CI bash脚本.我无法弄清楚如何调用普通的终端命令,如ls
或xcodebuild
#!/usr/bin/env xcrun swift
import Foundation // Works
println("Test") // Works
ls // Fails
xcodebuild -workspace myApp.xcworkspace // Fails
Run Code Online (Sandbox Code Playgroud)
$ ./script.swift
./script.swift:5:1: error: use of unresolved identifier 'ls'
ls // Fails
^
... etc ....
Run Code Online (Sandbox Code Playgroud)
rin*_*aro 114
如果你不在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")
Run Code Online (Sandbox Code Playgroud)
更新:适用于Swift3/Xcode8
use*_*009 47
如果您想在命令行中"完全"使用命令行参数(不分离所有参数),请尝试以下操作.
(这个答案改进了LegoLess的答案,可以在Swift 4 Xcode 9.3中使用)
func shell(_ command: String) -> String {
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String
return output
}
// Example usage:
shell("ls -la")
Run Code Online (Sandbox Code Playgroud)
Leg*_*ess 30
这里的问题是你不能混淆和匹配Bash和Swift.您已经知道如何从命令行运行Swift脚本,现在需要添加方法以在Swift中执行Shell命令.来自PracticalSwift博客的总结:
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)
return output
}
Run Code Online (Sandbox Code Playgroud)
以下Swift代码将xcodebuild
使用参数执行,然后输出结果.
shell("xcodebuild", ["-workspace", "myApp.xcworkspace"]);
Run Code Online (Sandbox Code Playgroud)
至于搜索目录内容(这是ls
Bash中的内容),我建议NSFileManager
直接在Swift中使用和扫描目录,而不是Bash输出,这可能很难解析.
小智 20
实用功能在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)
}
Run Code Online (Sandbox Code Playgroud)
Pel*_*let 16
如果您想使用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)
}
Run Code Online (Sandbox Code Playgroud)
例如,获取当前工作目录的当前工作git分支:
let currentBranch = bash("git", arguments: ["describe", "--contains", "--all", "HEAD"])
print("current branch:\(currentBranch)")
Run Code Online (Sandbox Code Playgroud)
完整的脚本基于Legoless的答案
#!/usr/bin/env swift
import Foundation
func printShell(launchPath: String, arguments: [String] = []) {
let output = shell(launchPath: launchPath, arguments: arguments)
if (output != nil) {
print(output!)
}
}
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)
return output
}
// > ls
// > ls -a -g
printShell(launchPath: "/bin/ls")
printShell(launchPath: "/bin/ls", arguments:["-a", "-g"])
Run Code Online (Sandbox Code Playgroud)
更新Swift 4.0(处理更改String
)
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.count > 0 {
//remove newline character.
let lastIndex = output.index(before: output.endIndex)
return String(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)
}
Run Code Online (Sandbox Code Playgroud)
小智 7
在尝试了此处发布的一些解决方案后,我发现执行命令的最佳方法是使用-c
参数的标志。
@discardableResult func shell(_ command: String) -> (String?, Int32) {
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", command]
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)
}
let _ = shell("mkdir ~/Desktop/test")
Run Code Online (Sandbox Code Playgroud)
自苹果不推荐使用.launchPath和launch()以来,只是为了对此进行更新,这是Swift 4的更新实用程序函数,应该可以在以后进行验证。
注意:Apple的替换文档(run(),executableURL等)目前基本上为空。
import Foundation
// wrapper function for shell commands
// must provide full path to executable
func shell(_ launchPath: String, _ arguments: [String] = []) -> (String?, Int32) {
let task = Process()
task.executableURL = URL(fileURLWithPath: launchPath)
task.arguments = arguments
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
do {
try task.run()
} catch {
// handle errors
print("Error: \(error.localizedDescription)")
}
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)
task.waitUntilExit()
return (output, task.terminationStatus)
}
// valid directory listing test
let (goodOutput, goodStatus) = shell("/bin/ls", ["-la"])
if let out = goodOutput { print("\(out)") }
print("Returned \(goodStatus)\n")
// invalid test
let (badOutput, badStatus) = shell("ls")
Run Code Online (Sandbox Code Playgroud)
应该可以将其直接粘贴到操场上以查看实际效果。