6

How can I run a shell script from cocoa application using Swift?

I have a shell script file.sh that I want to run from within my cocoa application. How can I do this using Swift?

Any help appreciated! :)

iSebbeYT
  • 1,441
  • 2
  • 18
  • 29

2 Answers2

12

Pre Swift 3

You can use NSTask (API reference here) for this.

A NSTask takes (among other things) a launchPath which points to your script. It can also take an array of arguments and when you are ready to launch your task, you call launch() on it.

So...something along the lines of:

var task = NSTask()
task.launchPath = "path to your script"
task.launch()

Post Swift 3

As @teo-sartory points out in his comment below NSTask is now Process, documented here

The naming and way you call it has changed a bit as well, here is an example of how to use Process to call ls

let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/ls")
try? process.run()

If you want better access to/more control over the output from your invocation, you can attach a Pipe (documented here).

Here is a simple example of how to use that:

let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/ls")

// attach pipe to std out, you can also attach to std err and std in
let outputPipe = Pipe()
process.standardOutput = outputPipe

// away we go!
try? process.run()

//read contents as data and convert to a string
let output = outputPipe.fileHandleForReading.readDataToEndOfFile()
let str = String(decoding: output, as: UTF8.self)
print(str)

Would you like to know more

You can have a look at:

Hope that helps you.

pbodsk
  • 6,787
  • 3
  • 21
  • 51
7

I found this function on the web:

@discardableResult
private func shell(_ args: String) -> String {
    var outstr = ""
    let task = Process()
    task.launchPath = "/bin/sh"
    task.arguments = ["-c", args]
    let pipe = Pipe()
    task.standardOutput = pipe
    task.launch()
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    if let output = String(data: data, encoding: .utf8) {
        outstr = output as String
    }
    task.waitUntilExit()
    return outstr
}

Here's the call:

shell("/pathToSh/file.sh")
Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
Cristian
  • 654
  • 1
  • 13
  • 30
  • That's wrong. The full command from your func is: `/bin/sh -c sh /pathToSh/file.sh` And this obviously doesn't work.. – Alessandro Ornano Apr 04 '22 at 07:48
  • @AlessandroOrnano just re-tested with `shell("open /Folder/")` and it worked just fine. Maybe you should check what `-c` does... https://stackoverflow.com/questions/3985193/what-is-bin-sh-c – Cristian Apr 04 '22 at 20:12
  • Even your second example is wrong, `open` opens file from a shell, don't change directory. Your method launch `bash -c` that mean it will launch bash and execute the command after -c, so it's like `bash -c bash file.sh` and it doesn't work. – Alessandro Ornano Apr 05 '22 at 06:19
  • I never said I wanted to change the directory, I wanted to open it and it did. So on that note, it works. Now, the command `sh` is meant to run the script... Your version works as well though. – Cristian Apr 05 '22 at 10:37