Mark Needham

Thoughts on Software Development

F#: Passing command line arguments to a script

with one comment

I’ve been doing a bit of refactoring of my FeedBurner application so that I can call it from the command line with the appropriate arguments and one of the problems I came across is working out how to pass arguments from the command line into an F# script.

With a compiled application we are able to make use of the ‘EntryPointAttribute‘ to get access to the arguments passed in:

[<EntryPointAttribute>]
let main args =
    ShowFeedBurnerStats args
    0

Sadly this doesn’t work with a script but it was pointed out on Hub FS that we can get access to all the command line arguments by using ‘Sys.argv’ or ‘System.Environment.GetCommandLineArgs()’ which seems to be the preferred choice of the compiler.

The problem is that with that method you get every single argument passed to the command line and there are some that we don’t care about given the way you would typically call an F# script:

fsi --exec --nologo CreateFeedBurnerGraph.fsx -- "scotthanselman" "2009-03-01" "2009-07-14"

Results in the following arguments:

fsi
--exec
--nologo
CreateFeedBurnerGraph.fsx
--
scotthanselman
2009-03-01
2009-07-14

We care about everything after the ‘–’ so I wrote a little function to just gather those values:

    let GetArgs initialArgs  =
        let rec find args matches =
            match args with
            | hd::_ when hd = "--" -> List.to_array (matches)
            | hd::tl -> find tl (hd::matches) 
            | [] -> Array.empty
        find (List.rev (Array.to_list initialArgs) ) []

I’m not sure this works for every possible case (if you put ‘–’ in as an argument it wouldn’t work as expected!) but it’s doing the job so far.

An even better way of doing this which I came across while writing this is to use ‘fsi.CommandLineArgs’ which allows you to just get the arguments passed to the script. Even with this approach though the ‘–’ is still counted as one of the arguments so the function above still makes sense.

GetArgs [|"--"; "scotthanselman"; "2009-03-01"; "2009-07-14"|]
val it : string array = [|"scotthanselman"; "2009-03-01"; "2009-07-14"|]

And from the script I have the following:

let programArgs = fsi.CommandLineArgs |> GetArgs
ShowFeedBurnerStats programArgs

Written by Mark Needham

July 16th, 2009 at 7:40 am

Posted in F#

Tagged with

  • http://jessicah.org/journal/ Jessica

    Hi Mark,

    Thought I’d just point a much simpler and efficient implementation of GetArgs :)

    Although efficiency isn’t that important with such a small data set, it’s useful to know how to do things like this better.

    let rec GetArgs = function
    | “–” :: rest -> List.to_array rest
    | _ :: tail -> GetArgs tail
    | [] -> Array.empty

    It also means your case of having two “–” works correctly too; all other “–” get included as actual arguments.