Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you access command line arguments in Swift?

Update 01/17/17: Updated the example for Swift 3. Process has been renamed to CommandLine.


Update 09/30/2015: Updated the example to work in Swift 2.


It's actually possible to do this without Foundation or C_ARGV and C_ARGC.

The Swift standard library contains a struct CommandLine which has a collection of Strings called arguments. So you could switch on arguments like this:

for argument in CommandLine.arguments {
    switch argument {
    case "arg1":
        print("first argument")

    case "arg2":
        print("second argument")

    default:
        print("an argument")
    }
}

In Swift 3 use CommandLine enum instead of Process

So:

let arguments = CommandLine.arguments

Use the top level constants C_ARGC and C_ARGV.

for i in 1..C_ARGC {
    let index = Int(i);

    let arg = String.fromCString(C_ARGV[index])
    switch arg {
    case "this":
        println("this yo");

    case "that":
        println("that yo")

    default:
        println("dunno bro")
    }
}

Note that I'm using the range of 1..C_ARGC because the first element of the C_ARGV "array" is the application's path.

The C_ARGV variable is not actually an array but is sub-scriptable like an array.


Anyone who wants to use the old "getopt" (which is available in Swift) can use this as reference. I made a Swift port of the GNU example in C one can find at:

http://www.gnu.org/software/libc/manual/html_node/Example-of-Getopt.html

with a full description. It's tested and fully functional. It doesn't require Foundation either.

var aFlag   = 0
var bFlag   = 0
var cValue  = String()

let pattern = "abc:"
var buffer = Array(pattern.utf8).map { Int8($0) }

while  true {
    let option = Int(getopt(C_ARGC, C_ARGV, buffer))
    if option == -1 {
        break
    }
    switch "\(UnicodeScalar(option))"
    {
    case "a":
        aFlag = 1
        println("Option -a")
    case "b":
        bFlag = 1
        println("Option -b")
    case "c":
        cValue = String.fromCString(optarg)!
        println("Option -c \(cValue)")
    case "?":
        let charOption = "\(UnicodeScalar(Int(optopt)))"
        if charOption == "c" {
            println("Option '\(charOption)' requires an argument.")
        } else {
            println("Unknown option '\(charOption)'.")
        }
        exit(1)
    default:
        abort()
    }
}
println("aflag ='\(aFlag)', bflag = '\(bFlag)' cvalue = '\(cValue)'")

for index in optind..<C_ARGC {
    println("Non-option argument '\(String.fromCString(C_ARGV[Int(index)])!)'")
}

Apple has released the ArgumentParser library for doing just this:

We’re delighted to announce ArgumentParser, a new open-source library that makes it straightforward — even enjoyable! — to parse command-line arguments in Swift.

https://swift.org/blog/argument-parser/


Swift Argument Parser

https://github.com/apple/swift-argument-parser

Begin by declaring a type that defines the information you need to collect from the command line. Decorate each stored property with one of ArgumentParser's property wrappers, and declare conformance to ParsableCommand.

The ArgumentParser library parses the command-line arguments, instantiates your command type, and then either executes your custom run() method or exits with useful a message.


You could create an argument parser by using the CommandLine.arguments Array and add any logic you like.

You can test it. Create a file arguments.swift

//Remember the first argument is the name of the executable
print("you passed \(CommandLine.arguments.count - 1) argument(s)")
print("And they are")
for argument in CommandLine.arguments {
    print(argument)
}

compile it and run it:

$ swiftc arguments.swift
$ ./arguments argument1 argument2 argument3

The issue with you building your own argument parser is taking into account all the command-line argument conventions. I would recommend using an existing Argument Parser.

You could use:

  • Vapor's Console module
  • TSCUtility Argument Parser used by the Swift Package manager
  • The Swift Argument Parser open-sourced by Apple

I've written about how to build command-line tools on all three. You should check them out and decide what style suits you best.

If you are interested here are the links:

  •  Building a CLI tool using Swift and Vapor's Console module 
  • Command-line argument parsing using Swift Package Manager's TSCUtility module 
  • Understanding the Swift Argument Parser and working with STDIN