Parsing command line options using Swift

There is a higher chance that you might have stayed away from the terminal or the command line, after all the GUI is so much better and easier to use. However if you are like me or Old School, then you might love the command line. In most cases the command-line like a function requires parameters and these are passed after the command name. The developer of that utility has to write code to check if the parameters were passed correctly. Say for example we write a command-line utility like in our previous article called notify. This takes the following parameters, a message followed by flags called ‘ –title ‘ and ‘ –after ‘. These parameters passed with the two hyphens are called long format. In case of the most commonly used command-line ls, you call it like ‘ls -l -a –color-mode’. The -l and the -a are called the short format. Let’s look at how we can parse these parameters in swift.

getOpts

It can be quite involved to write a parser to get the options in swift. A pure swift function or module would be amazing however there is a C API function called getopts . This works with only the short format codes. The flags that are required are passed that indicate if the flag is a setting by itself or requires an argument. Say in the case of our utility notify, we set -t as the short code for the longer format of–title and -a for the longer format of –after . Then we could call notify as ‘ notify message -t title -a 5

The getopts codes would look like ” a:t: ” the colon after the character signifies that it requires a parameter.

How to use it?

So rather than go into the mundane details of getopts , let’s look at using it. The first thing is that it is a C-API, and it requires its data in a non swift C fashion which is all pointers (that is why a Swift implementation of this would be such a good idea). So first you need to change the string arrays into UnsafeMutablePointers that you can pass to the C function.

let parameters = ["notify", "message", "-t", "title", "-a", "5"]
var pointer = parameters.map { strdup($0) }

while true {
    var result = getopt(parameters.count, &pointer, "a:t:")
    if result == -1 { break }
    
    switch case result {
        case 97:
            print("the value for after is \(optarg)")
        case 116:
            print("the value for title is \(optarg)")
        default:
            print("message")
    }
}

for ptr in pointer { free(ptr) }

In closing

Personally I am an advocate of using a pure swift solution, however in the case where there is a function that works, then why not use it. The only thing that is problematic, is that when you deal with pointers, you need to be careful otherwise you can end up with memory leaks.

Views All Time
Views All Time
1132
Views Today
Views Today
1
Posted in Article, Tip.