Saya ingin mengganti skrip CI bash saya dengan swift. Saya tidak tahu cara menjalankan perintah terminal normal seperti ls
atauxcodebuild
#!/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
bash
shell
xcodebuild
Robert
sumber
sumber
#!/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")
Jika Anda ingin menggunakan argumen baris perintah "persis" seperti yang Anda lakukan pada baris perintah (tanpa memisahkan semua argumen), coba yang berikut ini.
(Jawaban ini meningkatkan jawaban LegoLess dan dapat digunakan di Swift 5)
import Foundation func shell(_ command: String) -> String { let task = Process() let pipe = Pipe() task.standardOutput = pipe task.arguments = ["-c", command] task.launchPath = "/bin/zsh" task.launch() let data = pipe.fileHandleForReading.readDataToEndOfFile() let output = String(data: data, encoding: .utf8)! return output } // Example usage: shell("ls -la")
sumber
/bin/bash
merujuk kebash-3.2
. Jika Anda ingin menggunakan fitur-fitur bash yang lebih canggih, ubah jalurnya (/usr/bin/env bash
biasanya merupakan alternatif yang baik)Masalahnya di sini adalah Anda tidak dapat mencampur dan mencocokkan Bash dan Swift. Anda sudah tahu cara menjalankan skrip Swift dari baris perintah, sekarang Anda perlu menambahkan metode untuk menjalankan perintah Shell di Swift. Ringkasan dari blog 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 }
Kode Swift berikut akan dijalankan
xcodebuild
dengan argumen dan kemudian menampilkan hasilnya.shell("xcodebuild", ["-workspace", "myApp.xcworkspace"]);
Untuk mencari konten direktori (yang
ls
dilakukan di Bash), saya sarankan untuk menggunakanNSFileManager
dan memindai direktori secara langsung di Swift, daripada keluaran Bash, yang bisa sulit untuk diurai.sumber
shell("ls", [])
-'NSInvalidArgumentException', reason: 'launch path not accessible'
Ada ide?Fungsi utilitas di Swift 3.0
Ini juga mengembalikan status penghentian tugas dan menunggu sampai selesai.
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) }
sumber
import Foundation
hilangJika Anda ingin menggunakan lingkungan bash untuk memanggil perintah, gunakan fungsi bash berikut yang menggunakan Legoless versi tetap. Saya harus menghapus baris baru yang tertinggal dari hasil fungsi 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) }
Misalnya untuk mendapatkan cabang git yang berfungsi saat ini dari direktori kerja saat ini:
let currentBranch = bash("git", arguments: ["describe", "--contains", "--all", "HEAD"]) print("current branch:\(currentBranch)")
sumber
Skrip lengkap berdasarkan jawaban 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"])
sumber
Hanya untuk memperbarui ini karena Apple telah menghentikan .launchPath dan launch (), berikut adalah fungsi utilitas yang diperbarui untuk Swift 4 yang seharusnya menjadi bukti masa depan yang sedikit lebih baik.
Catatan: Dokumentasi Apple tentang penggantian ( run () , executableURL , dll) pada dasarnya kosong pada saat ini.
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")
Harus bisa menempelkannya langsung ke taman bermain untuk melihatnya beraksi.
sumber
Memperbarui untuk Swift 4.0 (menangani perubahan pada
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) }
sumber
Setelah mencoba beberapa solusi yang diposting di sini, saya menemukan bahwa cara terbaik untuk menjalankan perintah adalah menggunakan
-c
flag untuk argumen.@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")
sumber
Menggabungkan rintaro dan jawaban Legoless untuk 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 }
sumber
Perbaikan kecil dengan dukungan untuk variabel 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) }
sumber
Contoh penggunaan kelas Proses untuk menjalankan skrip Python.
Juga:
- 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)") } }
sumber
Saya telah membangun SwiftExec , perpustakaan kecil untuk menjalankan perintah seperti itu:
import SwiftExec var result: ExecResult do { result = try exec(program: "/usr/bin/git", arguments: ["status"]) } catch { let error = error as! ExecError result = error.execResult } print(result.exitCode!) print(result.stdout!) print(result.stderr!)
Ini adalah pustaka file tunggal yang dapat dengan mudah disalin-tempel ke dalam proyek atau diinstal menggunakan SPM. Ini diuji dan menyederhanakan penanganan kesalahan.
Ada juga ShellOut , yang juga mendukung berbagai perintah yang telah ditentukan sebelumnya.
sumber