如何在swift脚本中运行终端命令?(例如xcodebuild)

 寻找另一半哥哥_335 发布于 2022-12-10 23:53

我想用swift替换我的CI bash脚本.我无法弄清楚如何调用普通的终端命令,如lsxcodebuild

#!/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 ....

rintaro.. 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")

更新:适用于Swift3/Xcode8

8 个回答
  • 如果您想使用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)
    }
    

    例如,获取当前工作目录的当前工作git分支:

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

    2022-12-11 02:08 回答
  • 如果您想在命令行中"完全"使用命令行参数(不分离所有参数),请尝试以下操作.

    (这个答案改进了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")
    

    2022-12-11 02:08 回答
  • 这里的问题是你不能混淆和匹配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"]);
    

    至于搜索目录内容(这是lsBash中的内容),我建议NSFileManager直接在Swift中使用和扫描目录,而不是Bash输出,这可能很难解析.

    2022-12-11 02:09 回答
  • 自苹果不推荐使用.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")
    

    应该可以将其直接粘贴到操场上以查看实际效果。

    2022-12-11 02:09 回答
  • 完整的脚本基于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"])
    

    2022-12-11 02:10 回答
  • 实用功能在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)
    }
    

    2022-12-11 02:11 回答
  • 如果你不在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

    2022-12-11 02:56 回答
  • 更新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)
    }
    

    2022-12-11 03:12 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有