【问题标题】:How to run terminal command in swift from any directory?如何从任何目录快速运行终端命令?
【发布时间】:2020-08-23 23:01:37
【问题描述】:

我正在尝试创建一个涉及允许用户运行终端命令的 macOS 应用程序。我可以运行命令,但据我所知,它是从我的应用程序内的目录运行的。运行pwd 返回/Users/<me>/Library/Containers/<My app's bundle identifier>/Data。

如何选择命令从哪个目录运行?

我也在寻找让cd 工作的方法,但如果我可以选择从哪个目录运行终端命令,我可以手动处理cd。

这是我目前用来运行终端命令的代码:

func shell(_ command: String) -> String {
    let task = Process()
    let pipe = Pipe()

    task.standardOutput = pipe
    task.arguments = ["-c", command]
    task.launchPath = "/bin/zsh"
    task.launch()

    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: .utf8)!

    return output
    
}

我在 Big Sur 上使用 Xcode 12。

谢谢!

【问题讨论】:

    标签: swift terminal zsh macos-big-sur


    【解决方案1】:

    Process 上有一个已弃用的属性currentDirectoryPath。

    假设您不想使用已弃用的属性,请在阅读其文档后转到 FileManager 并查看管理当前目录的规定及其影响。

    或者只是使用 cd,正如您所考虑的那样 - 您正在启动一个带有 shell 命令行 作为参数的 shell (zsh)。一个命令行可以包含多个用分号分隔的命令,因此您可以在您的 command 值前添加一个 cd。

    后一种方法避免更改当前进程的当前目录。

    HTH

    【讨论】:

    • 感谢您的回复。我对 swift 比较陌生,FileManager 大部分时间都在我头上。关于如何准确使用它的一些帮助会很棒。当我提到cd 时,我只是想让命令为用户工作,因为它实际上并没有更改目录,所以我想我可以捕获任何cds,并用我自己的方式更改和跟踪 cwd代码。使用cds 来更改目录似乎不必要的复杂,我想尝试使用FileManager。我在测试时也遇到了权限问题。当我 cd'd 到主目录并执行 ls 时,我恢复了 Operation not permitted。
    • 您有 2 个不同的问题 (a) 从您的问题更改当前目录 - 您已使用 cd 完成了此操作,这是一个很好的方法; (b) 您在评论中提到的权限问题。后者是由于 sandbox 的限制,您不能只从应用程序移动文件系统中的任何位置并访问文件/执行命令。您需要研究沙盒并确定您尝试做的事情是否允许,是否可以关闭沙盒等。这里是a page,您可以从这里开始。
    • 我更喜欢使用FileManager,因为它似乎会使我的代码更简单,尽管我不知道如何使用它来做我想做的事情。你能指出我正确的方向吗?如果我最终需要在每个命令的开头使用cd,则不推荐使用“所有文件权利”属性,这就是我猜想我需要使用的属性。有什么我需要使用的吗?使用其他方法甚至可以避免这个问题吗?
    • 使用FileManager 与cd 对您的应用程序对文件系统的访问没有影响——您需要了解沙盒,是否应该使用它以及限制和关闭的风险等。安全性的存在是有原因的。使用FileManager,它只是一个与任何其他类一样的类,不会使您的代码更简单本身,但这意味着当前目录将针对您的整个应用程序而改变,而不仅仅是你正在运行的 shell 命令——你需要弄清楚这对你的情况是好是坏。
    • 啊,好吧,我明白了。那么我究竟该如何关闭沙盒呢?更重要的是,它说你必须有沙盒才能通过 App Store 分发应用程序......除了我自己分发应用程序之外,还有什么办法解决这个问题?
    【解决方案2】:

    为了补充 CRD 的答案,如果使用 cd 方法,您还可以考虑使用 && 分隔您的命令,以等待前面的命令成功完成,然后再继续执行依赖它的下一个命令。

    尝试你希望在终端中运行的命令,看看它是否按预期完成

    例如:/bin/bash -c "cd /source/code/ && git pull && swift build"

    如果一切正常,您可以继续在您的 swift 代码中使用它,如下所示:
    shell("cd /source/code/ && git pull && swift build")


    关于弃用的主题,您可能想要替换 带有executableURL 的启动路径 和 使用run()启动()

    带有更新代码的示例实现:

    @discardableResult
    func shell(_ args: String...) -> Int32 {
        let task = Foundation.Process()
        
        task.executableURL = URL(fileURLWithPath: "/bin/bash")
        task.arguments = ["-c"]
        task.arguments = task.arguments! + args
        
        //Set environment variables
        var environment = ProcessInfo.processInfo.environment
        environment["PATH"]="/usr/bin/swift"
        //environment["CREDENTIALS"] = "/path/to/credentials"
        task.environment = environment
        
        let outputPipe = Pipe()
        let errorPipe = Pipe()
        
        task.standardOutput = outputPipe
        task.standardError = errorPipe
        
        do {
            try task.run()
        } catch {
            // handle errors
            print("Error: \(error.localizedDescription)")
        }
        task.waitUntilExit()
        
        let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
        let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
        
        let output = String(decoding: outputData, as: UTF8.self)
        let error = String(decoding: errorData, as: UTF8.self)
        
        //Log or return output as desired
        print(output)
        print("Ran into error while running: \(error)")
        
        return task.terminationStatus
    }
    

    【讨论】:

      猜你喜欢
      • 2017-08-18
      • 2022-08-03
      • 1970-01-01
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      • 2012-12-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多