1

I'm attempting to run a Swift script using the command line from my Swift-based Mac app.

I have the following class method, which takes in arguments, and runs the commands:

func shell(_ args: String...) -> Int32 { let task = Process() task.launchPath = "/usr/bin/env" task.arguments = args task.launch() task.waitUntilExit() return task.terminationStatus } 

I'm able to execute commands such as the following successfully:

 shell("pwd") shell("ls") shell("swift") 

pwd returns all the files in my app's build directory, as expected. This includes a hello.swift file that I manually added, which just prints "Hello, world!". Additionally, running swift does grant access to the Swift environment.

What I'm not having luck with is running commands such as:

shell("swiftc hello.swift")

Instead, I get the following error:

env: swiftc hello.swift: No such file or directory

It looks as though I'm facing a similar situation as these posts:

Swift Process - execute command error

Running shell commands in Swift

But, I'm not sure I fully understand all the implications therein for my specific situation.

2
  • 1
    Never knew Swift was a scripting language - much less had those capabilities. Always thought invoked the compiler, which is quite different than invoking a script. Always knew you could compile something in most any language using shell commands, but always thought the output of compiling wasn't a script. Bottom line? I think you are hoping to make an "Apple" into an "Orange". What makes you think there's anything like a "Swift scripting language"? Commented Jan 15, 2018 at 16:40
  • 1
    @dfd the "Apple" needs no conversion to your "Orange". Swift is perfectly suited as a scripting language and has been since version 1, as demonstrated here. It's also very powerful, since Anything you can do with Foundation, you can put into a [swift] script." Obviously, we've moved on from v1, but it's still viable today Commented Jan 16, 2018 at 8:53

1 Answer 1

2

Just to clarify, before we begin, swiftc is used to compile a swift script into a binary. In contrast, calling swift with a swift script file, will interpret the given file.

env: swiftc hello.swift: No such file or directory

Essentially, this is stating that the env binary is being passed two arguments: swiftc and hello.swift and doesn't know what to do with it.

task.launchPath = "/usr/bin/env" 

I'm not sure why you're calling env here, but assuming I understand your goal correctly, we can use bash for the desired outcome.

Let's assume we have the following script.swift file

#!/usr/bin/swift import Foundation func shell(_ args: String...) -> Int32 { let task = Process() task.launchPath = "/bin/bash" task.arguments = ["-c"] task.arguments = task.arguments! + args task.launch() task.waitUntilExit() return task.terminationStatus } _ = shell("pwd") _ = shell( "swift cmds.swift") 

Instead of calling env, it's using bash. In order to pass a string to bash it requires the -c argument, which we prepended with

task.arguments = ["-c"] task.arguments = task.arguments! + args 

The end of the script can be seen calling the file cmds.swift. If we execute script.swift via the interpreter, it's not going to be able to call cmds.swift - essentially calling the interpreter from within itself!

So, we'll compile script.swift to a binary:

swiftc script.swift 

This outputs a binary with the name script.

As we've seen, the binary calls cmds.swift, so let's create this with the following script code...

#!/usr/bin/swift import Foundation print("Hello World\n") 

Now if we execute the binary that we compiled, we'll see it successfully call the interpreted script and output the path (from pwd) and "Hello World", which came from the calling cmds.swift.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.