'illegal option -- c' 尝试 运行 来自 Swift 的终端命令

'illegal option -- c' trying to run a terminal command from Swift

好的,所以我正在尝试从 Swift MacOS 应用程序 运行 bash 中的 Mosquitto 发布命令。这是我的代码:

@IBAction func buttonClicked(_ sender: Any) {
        let mosquittoCommand = "mosquitto_pub --cert blahblah.pem --key blahblah.key --cafile blahblah.pem -h 'blah.blah.com' -p 443 -t 'blah/blah/blah/blah' -m '{\"msg\": \"blahblahblah\", \"time\": \"2019-08-07T15:12:00Z\", \"id\": \"blah-blah-blah\", \"localpwd\": \"blahblahblah\"}' --tls-alpn x-amzn-mqtt-ca -i 'blahblahblah'"

        print(shell("cd /Users/Me/Desktop/certs && " + mosquittoCommand))
    }

    func shell(_ command: String) -> String {
        let task = Process()
        task.launchPath = "/usr/bin/env"
        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
    }

我收到以下错误:

/usr/bin/env: illegal option -- c
usage: env [-iv] [-P utilpath] [-S string] [-u name]
           [name=value ...] [utility [argument ...]]

您必须相信我,运行在终端 window 中执行命令可以直接按预期工作。唯一的区别是 mosquitto 命令中的转义字符,以防止引号弄乱命令。也许转义字符导致了问题?

我不知道错误试图告诉我什么。任何建议将不胜感激。谢谢。

编辑 - 我已经确定从 Swift 链接一些基本命令(pwd、cd...等)是有效的。所以它肯定设置正确,能够 运行 这样的命令,我只是不知道为什么它不能 运行 Mosquitto 发布命令。

错误消息的直接原因是 /usr/bin/env 没有 -c 选项,显然您将其与 /bin/bash -c "command ..." 混淆了。此外,env 命令只能启动单个可执行文件,不能启动与 && 链接在一起的多个命令。

另一个问题是当 运行 您的应用程序从 Finder 中找不到时 mosquitto_pub 二进制文件。正如讨论中所证明的那样,该程序安装(通过 Homebrew)在 /usr/local/bin 中。该目录通常位于终端 shell 的搜索路径中,但当应用程序从 Finder 启动时则不存在。

为程序使用绝对路径是一种选择:

let mosquittoCommand = "/usr/local/bin/mosquitto_pub --cert blahblah.pem ..."

如评论中所述,将启动路径(绝对路径)、执行命令的工作目录以及命令参数设置为数组更容易:

let task = Process()
task.launchPath = "/usr/local/bin/mosquitto_pub"
task.arguments = [
    "--cert",
    "blahblah.pem",
    // ...
    "-i",
    "blahblahblah"
]
task.currentDirectoryPath = "/Users/Me/Desktop/certs"

这使得引用参数和通过 shell 调用变得不必要。

或者您可以通过 env 启动该程序(以便在多个可能的位置找到它)但是随后您将“/usr/local/bin”添加到搜索路径:

// Add "/usr/local/bin" to search path:
var env = task.environment ?? [:]
if let path = env["PATH"] {
    env["PATH"] = "/usr/local/bin:" + path
} else {
    env["PATH"] = "/usr/local/bin"
}
task.environment = env

task.launchPath = "/usr/bin/env"
task.arguments = [
    "mosquitto_pub",
    "--cert",
    "blahblah.pem",
    // ...
    "-i",
    "blahblahblah"
]
task.currentDirectoryPath = "/Users/Me/Desktop/certs"

终于可以使用了

task.currentDirectoryURL = FileManager.default
    .homeDirectoryForCurrentUser
    .appendingPathComponent("certs")

使工作目录适用于任何用户名。