Home > Net >  executing a shell script located in app bundle in MacOS with swift
executing a shell script located in app bundle in MacOS with swift

Time:03-02

I'm trying to find out if is it possible to execute a shell script located in my app bundle in swift. It is a Mac application with Sandbox disabled.

This is how I get the url and it is working:

guard let saveScriptURL = Bundle.main.url(forResource: "scripts/save", withExtension: "sh") else {
            VsLogger.logDebug("***", "Unable to get save.sh file")
            return false
        }

which returns this

/Users/me/Library/Developer/Xcode/DerivedData/appName-fcowyecjzsqnhrchpnwrtthxzpye/Build/Products/Debug/appName.app/Contents/Resources/scripts/save.sh

then this is my code to run it.

  func shell(_ scriptURL: URL) throws {
        let task = Process()
        let pipe = Pipe()
        task.standardOutput = pipe
        task.standardError = pipe
        task.executableURL =  scriptURL
        try task.run()
    }

but I get the error:

Error Domain=NSCocoaErrorDomain Code=4 "The file “save.sh” doesn’t exist." UserInfo={NSFilePath=/Users/me/Library/Developer/Xcode/DerivedData/appName-fcowyecjzsqnhrchpnwrtthxzpye/Build/Products/Debug/appName.app/Contents/Resources/scripts/save.sh}

Any pointers are appreciated.

CodePudding user response:

There are some issues with your code that needs to be fixed.

Firstly you are using Process incorrectly, the property executableURL is meant for the executable, that is the shell in this case, you want to use for running your script so for zsh it should be set to

task.executableURL = URL(fileURLWithPath: "/bin/zsh")

Secondly it seems after some trial and error that we can not execute the script directly, I assume this is because even if we set the script as executable using chmod this is lost when the script is copied to the bundle. So the script needs to be run as "source save.sh"

To set the script to be run we use the arguments property

task.arguments = ["-c", "source \(scriptURL.path"]

So together your shell function becomes

func shell(_ scriptURL: URL) throws {
    let task = Process()
    let pipe = Pipe()
    task.standardOutput = pipe
    task.standardError = pipe

    task.executableURL = URL(fileURLWithPath: "/bin/zsh")
    task.arguments = ["-c", "source \(scriptURL.path)"]
    try task.run()
}
  • Related