Jak uruchomić polecenie terminal w skrypcie swift? (np. xcodebuild)

Chcę zastąpić Skrypty CI bash swift. Nie wiem, jak wywołać zwykłe polecenie terminala, takie jak ls lub 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 ....
Author: Moritz, 2014-11-17

8 answers

Jeśli nie używasz poleceń w kodzie Swift, wystarczy:

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

Aktualizacja: dla Swift3 / Xcode8

 85
Author: rintaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-02 03:25:59

Problem polega na tym, że nie można mieszać i dopasowywać Basha i Swifta. Już wiesz, jak uruchomić skrypt Swift z linii poleceń, teraz musisz dodać metody do wykonywania poleceń powłoki w języku Swift. W podsumowaniu z 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
}

Następujący kod Swift wykona {[2] } z argumentami, a następnie wyświetli wynik.

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

Jeśli chodzi o Przeszukiwanie zawartości katalogu (czyli to, co robi ls w Bash), sugeruję użycie NSFileManager i skanowanie katalogu bezpośrednio w Swift, zamiast wyjścia Bash, co może być ból do analizy.

 26
Author: Legoless,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-24 00:23:48

Funkcja użytkowa w Swift 3.0

To również zwraca status zakończenia zadań i czeka na zakończenie.

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
Author: Arun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-07 23:13:09

Jeśli chcesz używać środowiska bash do wywoływania poleceń, użyj poniższej funkcji bash, która używa stałej wersji Legoless. Musiałem usunąć końcową linię nowego wiersza z wyniku funkcji powłoki.

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

Na przykład, aby uzyskać bieżącą działającą gałąź git bieżącego katalogu roboczego:

let currentBranch = bash("git", arguments: ["describe", "--contains", "--all", "HEAD"])
print("current branch:\(currentBranch)")
 15
Author: Pellet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-10 06:12:09

Pełny skrypt na podstawie odpowiedzi 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
Author: Robert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-17 13:04:35

Jeśli chcesz użyć argumentów wiersza poleceń "dokładnie" tak, jak w wierszu poleceń (bez oddzielania wszystkich argumentów), spróbuj wykonać następujące czynności.

W przeciwieństwie do innych języków, nie jest to Język Angielski.]}
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")
 9
Author: user3064009,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-26 05:08:52

Aktualizacja dla Swift 4.0 (dotyczy zmian do 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)
}
 7
Author: rougeExciter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-28 12:15:41

Mieszanie odpowiedzi rintaro i Legoless dla 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
Author: richy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-30 15:55:57