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()
}