You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift macOS命令行工具调用Homebrew安装的kotlinc命令报错

问题背景

我正在使用Swift为应用开发macOS平台的Command Line Tool Target,需要在该Target中执行通过Homebrew安装的Kotlin命令。

待执行的Kotlin命令如下:

kotlinc -script ./kotlin_file.kts

该命令直接在终端中启动运行时无任何问题,但从Xcode运行该Command Line Tool Target时会触发报错,不同实现方式对应不同报错信息,已尝试多种实现方法,具体如下:

方法1

调用代码:

let test = execute1("kotlinc",
                    args: ["-script", "./kotlin_file.kts"])

实现逻辑:

private func execute1(command: String, args: [String] = []) -> String {
    let proc = Process()
    proc.launchPath = command
    proc.arguments = args

    let pipe = Pipe()
    proc.standardOutput = pipe

    proc.launch()

    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    return String(data: data, encoding: String.Encoding.utf8)!
}

报错信息:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'launch path not accessible'

方法2

调用代码:

let test = execute2("kotlinc",
                    args: ["-script", "./kotlin_file.kts"])

实现逻辑:

private func execute2(_ path: String, args: [String] = []) {
    let process = Process()
    process.executableURL = URL(fileURLWithPath: path)
    process.arguments = args

    let outputPipe = Pipe()
    process.standardOutput = outputPipe

    try? process.run()

    let output = outputPipe.fileHandleForReading.readDataToEndOfFile()
    let str = String(decoding: output, as: UTF8.self)
    print(str)
}

问题:程序会卡在readDataToEndOfFile步骤持续无响应。

方法3

将Kotlin命令写入script.sh文件后在Target中执行该shell文件,触发如下报错:

kotlinc: command not found

解决方案

该需求可以实现,所有报错的核心原因有两个:

  1. 从Xcode启动的Command Line Tool不会加载终端默认的shell环境变量,PATH中不包含Homebrew的二进制路径(Apple Silicon Mac默认是/opt/homebrew/bin,Intel Mac默认是/usr/local/bin),因此找不到kotlinc命令。
  2. 没有同时重定向标准错误输出,进程往stderr写内容时如果管道缓冲区满就会阻塞,导致卡在读取输出的步骤。

正确实现代码

import Foundation

@discardableResult
func runCommand(_ command: String, args: [String] = [], workingDirectory: String? = nil) throws -> (stdout: String, stderr: String, exitCode: Int32) {
    let process = Process()
    // 通过zsh执行命令,自动适配shell环境
    process.executableURL = URL(fileURLWithPath: "/bin/zsh")
    // 提前将Homebrew路径加入PATH兜底,拼接完整执行命令
    let fullCommand = "export PATH=\"/opt/homebrew/bin:/usr/local/bin:$PATH\" && \(command) \(args.joined(separator: " "))"
    process.arguments = ["-c", fullCommand]
    
    // 配置脚本所在工作目录
    if let workingDirectory = workingDirectory {
        process.currentDirectoryURL = URL(fileURLWithPath: workingDirectory)
    }

    let stdoutPipe = Pipe()
    let stderrPipe = Pipe()
    process.standardOutput = stdoutPipe
    process.standardError = stderrPipe

    try process.run()

    // 同时读取标准输出和标准错误,避免管道阻塞
    let stdoutData = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
    let stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile()
    
    process.waitUntilExit()

    let stdout = String(data: stdoutData, encoding: .utf8) ?? ""
    let stderr = String(data: stderrData, encoding: .utf8) ?? ""
    return (stdout, stderr, process.terminationStatus)
}

// 调用示例
do {
    let result = try runCommand("kotlinc", args: ["-script", "./kotlin_file.kts"])
    print("标准输出:\(result.stdout)")
    if result.exitCode != 0 {
        print("执行错误:\(result.stderr)")
    }
} catch {
    print("进程启动失败:\(error.localizedDescription)")
}

注意事项

  • 如果需要执行shell脚本,直接将命令参数替换为脚本路径即可,比如runCommand("./script.sh"),执行前需要给脚本添加可执行权限,终端执行chmod +x script.sh即可。
  • 如果kotlin_file.kts不在Xcode产物的默认执行目录下,要么传入文件绝对路径,要么在调用runCommand时通过workingDirectory参数指定脚本所在目录,避免找不到脚本文件。
  • 不要用try?吞掉process.run()的抛出错误,启动失败时可以直接拿到错误信息快速排查问题。

内容的提问来源于stack exchange,提问作者Xys

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 13:12:24