Domanda Come posso eseguire un comando terminale in uno script rapido? (ad esempio xcodebuild)


Voglio sostituire i miei script bash CI con swift. Non riesco a capire come richiamare il normale comando terminale come ls o 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 ....

46
2017-11-17 11:02


origine


risposte:


Se non si utilizzano le uscite di comando nel codice Swift, seguire sarebbe sufficiente:

#!/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")

Aggiornato: per Swift3 / Xcode8


81
2017-11-17 13:05



Il problema qui è che non è possibile combinare Bash e Swift. Sai già come eseguire lo script Swift dalla riga di comando, ora devi aggiungere i metodi per eseguire i comandi Shell in Swift. In sintesi da PracticalSwift blog:

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
}

Il seguente codice Swift verrà eseguito xcodebuild con argomenti e quindi restituire il risultato.

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

Per quanto riguarda la ricerca dei contenuti della directory (che è cosa ls fa in Bash), suggerisco di usare NSFileManager e la scansione della directory direttamente in Swift, invece di Bash, che può essere un problema da analizzare.


26
2017-11-17 11:48



Funzione di utilità In Swift 3.0

Ciò restituisce anche lo stato di terminazione delle attività e attende il completamento.

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)
}

18
2017-09-07 07:49



Se si desidera utilizzare l'ambiente bash per chiamare i comandi, utilizzare la seguente funzione bash che utilizza una versione fissa di Legoless. Ho dovuto rimuovere una nuova riga finale dal risultato della funzione 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)
}

Ad esempio, per ottenere il ramo git corrente della directory di lavoro corrente:

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

14
2017-07-20 07:26



Copione completo basato sulla risposta di 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"])

9
2017-11-17 12:58



Aggiornamento per Swift 4.0 (relativo alle modifiche a 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)
}

6
2018-01-28 12:15



Se si desidera utilizzare gli argomenti della riga di comando "esattamente" come si farebbe nella riga di comando (senza separare tutti gli argomenti), provare quanto segue.

(Questa risposta migliora la risposta di LegoLess e può essere utilizzata in 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")

3
2018-04-26 04:53



Mescolando rintaro e le risposte di Legoless per 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
}

0
2017-08-30 15:55