신속한 스크립트에서 터미널 명령을 어떻게 실행합니까? (예 : xcodebuild)
내 CI bash 스크립트를 신속하게 교체하고 싶습니다. ls
또는 같은 일반 터미널 명령을 호출하는 방법을 알 수 없습니다.xcodebuild
#!/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 용
모든 인수를 분리하지 않고 명령 줄에서와 같이 "정확하게"명령 줄 인수를 사용하려면 다음을 시도하십시오.
(이 답변은 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")
여기서 문제는 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
}
다음 Swift 코드는 xcodebuild
인수로 실행 된 다음 결과를 출력합니다.
shell("xcodebuild", ["-workspace", "myApp.xcworkspace"]);
디렉터리 내용 ( ls
Bash에서 수행하는 작업)을 NSFileManager
검색하려면 구문 분석이 어려울 수있는 Bash 출력 대신 Swift에서 직접 디렉터리를 사용 하고 검색하는 것이 좋습니다 .
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 환경을 사용하려면 고정 된 버전의 Legoless를 사용하는 다음 bash 함수를 사용하십시오. 쉘 함수의 결과에서 후행 개행 문자를 제거해야했습니다.
스위프트 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"])
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)
}
Apple이 .launchPath와 launch ()를 모두 사용하지 않기 때문에 이것을 업데이트하기 위해 조금 더 미래의 증거가 될 Swift 4 용 업데이트 된 유틸리티 함수가 있습니다.
참고 : 대체 항목 ( run () , executableURL 등) 에 대한 Apple의 문서 는 기본적으로이 시점에서 비어 있습니다.
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")
이것을 플레이 그라운드에 직접 붙여 넣어 실제 동작을 볼 수 있어야합니다.
Swift 3에 대한 rintaro와 Legoless의 답변 혼합
@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
}
env 변수 지원으로 약간의 개선 :
func shell(launchPath: String,
arguments: [String] = [],
environment: [String : String]? = nil) -> (String , Int32) {
let task = Process()
task.launchPath = launchPath
task.arguments = arguments
if let environment = environment {
task.environment = environment
}
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)
}
Process 클래스를 사용하여 Python 스크립트를 실행하는 예입니다.
또한:
- added basic exception handling
- setting environment variables (in my case I had to do it to get Google SDK to authenticate correctly)
- arguments
import Cocoa
func shellTask(_ url: URL, arguments:[String], environment:[String : String]) throws ->(String?, String?){
let task = Process()
task.executableURL = url
task.arguments = arguments
task.environment = environment
let outputPipe = Pipe()
let errorPipe = Pipe()
task.standardOutput = outputPipe
task.standardError = errorPipe
try task.run()
let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
let output = String(decoding: outputData, as: UTF8.self)
let error = String(decoding: errorData, as: UTF8.self)
return (output,error)
}
func pythonUploadTask()
{
let url = URL(fileURLWithPath: "/usr/bin/python")
let pythonScript = "upload.py"
let fileToUpload = "/CuteCat.mp4"
let arguments = [pythonScript,fileToUpload]
var environment = ProcessInfo.processInfo.environment
environment["PATH"]="usr/local/bin"
environment["GOOGLE_APPLICATION_CREDENTIALS"] = "/Users/j.chudzynski/GoogleCredentials/credentials.json"
do {
let result = try shellTask(url, arguments: arguments, environment: environment)
if let output = result.0
{
print(output)
}
if let output = result.1
{
print(output)
}
} catch {
print("Unexpected error:\(error)")
}
}
'Programing' 카테고리의 다른 글
iPhone 앱에서 카메라의 존재를 감지 하시겠습니까? (0) | 2020.11.29 |
---|---|
오류 403 : 액세스가 구성되지 않았습니다. (0) | 2020.11.29 |
문자열에서 문자 발생을 계산하는 간단한 방법 (0) | 2020.11.29 |
Xcode 6 iPhone 시뮬레이터 용 iOS 8 UITableView에서 SeparatorInset 제거 (0) | 2020.11.29 |
AndroidJUnit4 및 ActivityTestRule을 단위 테스트 클래스로 가져올 수없는 이유는 무엇입니까? (0) | 2020.11.29 |