Tuesday, April 7, 2015

Swift as a scripting language

I have been using Swift since it was first announced at the WWDC in 2014 and I can honestly say that it has quickly become my favorite programming language.  One of the really great features of Swift is its ability to be used not only as a compiled language but also as a scripting language.  This allows us to execute Swift scripts without the need to compile them first and outside of XCode.  In this post we will look at how to create and execute Swift scripts.

Lets start off by creating a simple Hello World script that will print the standard “Hello World” message to the console.  We will start by opening our favorite text editor (for me that is VI or UltraEdit) and create a file named hello.swift which contains the following code:

#!/usr/bin/env xcrun swift
println(“Hello World”)

The file, as it is currently saved, is not executable. Therefore before we can run it we need to make it executable.  To do this we need to open a terminal window (Applications->Utilities->Terminal) and change to the directory that our hello.swift file is in.  We then execute the following command to make the file executable:

chmod +x hello.swift

Now the file is executable and we can execute it with the following command from the terminal prompt in the directory that the hello.swift file is located in:

./hello.swift

If we run this command we should see the “Hello World” message printed to the console.  This is pretty cool but also pretty basic.  Now the question is can we accept a command line arguments and change the greeting to say hello to different people?  Of course we can. 

Similar to other languages, Swift creates an argument count variable and an argument value array that tell us the number of command line arguments and also the values.  These are:
  • C_ARGC:  The number of arguments passed in
  • C_ARGV:  An array of the arguments values

Lets see how to use these by creating another file named hello2.swift and put the following code in it.

#!/usr/bin/env xcrun swift

var name = "World"
if (C_ARGC > 1) {
    if let arg1 = String.fromCString(C_ARGV[1]) {
        name = arg1
    }
}
println("Hello \(name)")

Don’t forget to make the script executable using the chmod command that we showed earlier.
We begin this script by creating a variable named name which contains the string “World” by default.  

We then see if any command line argument were entered.  Notice that we are checking to see if there was more than one command line argument entered because the first command line argument is always the name of the file that we are running therefore the second command line argument would contain the name of the person to greet.  For example, if we ran the script like this:

./hello2.swift one two three

The C_ARGC[] array would contain the following data:
  • C_ARGC[0] = ./hello2.swift
  • C_ARGC[1] = one
  • C_ARGC[2] = two
  • C_ARGC[3] = three

If we have more than one command line argument we then use the fromCString() method to convert the command line argument to a Swift String.  We can use the fromCString() method because of the automatic bridging between the Swift String type and the NSString class.  We then set the name variable to this new string.

Finally we print out the greeting to the console.  The following example shows how we would run this script from a terminal prompt:

./hello2.swift Jon

The following example would print out the message “Hello Jon” to the console.

Lets look at one final example.  This example will accept one command line argument, which should be the path and file name of a text file.  The script will then print out the contents of the file to the console.  This is similar to the basic functionality of the cat command therefore we will call this script cat.swift.  Here is the code for the script. 

#!/usr/bin/env xcrun swift

import Foundation
let file = ""

if (C_ARGC > 1) {
    if let file = String.fromCString(C_ARGV[1]) {
        if let fileContent = NSString(contentsOfFile: file, encoding: NSUTF8StringEncoding, error: nil){

            println(fileContent)
        }
    }

} else {
    println("Requires a filename")
}

This example is similar to the hello2.swift example accept instead of printing a greeting to the console we create a String type that contains the contents of a file.  To do this we use the init(contentsOfFile,encoding,error) initiator to create the String type.  We then print the contents of that file to the console.

I spent many years as a Unix/Linux System Administer and this required me to become very adept at creating shell scripts.  While shell scripts and other scripting languages can be very powerful, they also have limitations.  Using Swift as a scripting language, with the Foundation library and Cocoa APIs, opens up all kinds of scripting possibilites.  It also allows us to write scripts in the same language that we use for our OS X and iOS development.


2 comments:

  1. C_ARGC has been renamed Process.argc
    C_ARGV has been renamed Process.unsafeArgv

    ReplyDelete
  2. I'm having fun with #!/usr/bin/swift
    Just like Perl, it has to compile the first time but is very quick on subsequent runs.

    ReplyDelete