Command Line Flags in Erlang

Here’s the translation of the Go code example to Erlang, formatted in Markdown suitable for Hugo:

Command-line flags are a common way to specify options for command-line programs. For example, in wc -l the -l is a command-line flag.

Erlang doesn’t have a built-in package for parsing command-line flags, but we can implement a simple parser ourselves. We’ll use this to implement our example command-line program.

-module(command_line_flags).
-export([main/1]).

parse_args([], Acc) -> Acc;
parse_args(["-" ++ Flag | Rest], Acc) ->
    case Flag of
        "word=" ++ Value -> parse_args(Rest, Acc#{word => Value});
        "numb=" ++ Value -> parse_args(Rest, Acc#{numb => list_to_integer(Value)});
        "fork" -> parse_args(Rest, Acc#{fork => true});
        "svar=" ++ Value -> parse_args(Rest, Acc#{svar => Value});
        _ -> io:format("Unknown flag: ~s~n", [Flag]), parse_args(Rest, Acc)
    end;
parse_args([Arg | Rest], Acc) ->
    Tail = maps:get(tail, Acc, []),
    parse_args(Rest, Acc#{tail => Tail ++ [Arg]}).

main(Args) ->
    % Set default values
    Defaults = #{
        word => "foo",
        numb => 42,
        fork => false,
        svar => "bar"
    },
    
    % Parse command-line arguments
    ParsedArgs = parse_args(Args, Defaults),
    
    % Print the parsed options and any trailing positional arguments
    io:format("word: ~s~n", [maps:get(word, ParsedArgs)]),
    io:format("numb: ~p~n", [maps:get(numb, ParsedArgs)]),
    io:format("fork: ~p~n", [maps:get(fork, ParsedArgs)]),
    io:format("svar: ~s~n", [maps:get(svar, ParsedArgs)]),
    io:format("tail: ~p~n", [maps:get(tail, ParsedArgs, [])]).

To experiment with the command-line flags program, first compile it and then run the resulting binary directly.

$ erlc command_line_flags.erl

Try out the compiled program by first giving it values for all flags.

$ erl -noshell -s command_line_flags main -word=opt -numb=7 -fork -svar=flag -s init stop
word: opt
numb: 7
fork: true
svar: flag
tail: []

Note that if you omit flags they automatically take their default values.

$ erl -noshell -s command_line_flags main -word=opt -s init stop
word: opt
numb: 42
fork: false
svar: bar
tail: []

Trailing positional arguments can be provided after any flags.

$ erl -noshell -s command_line_flags main -word=opt a1 a2 a3 -s init stop
word: opt
numb: 42
fork: false
svar: bar
tail: [a1,a2,a3]

Note that our simple parser treats all arguments after a non-flag argument as positional arguments.

$ erl -noshell -s command_line_flags main -word=opt a1 a2 a3 -numb=7 -s init stop
word: opt
numb: 42
fork: false
svar: bar
tail: [a1,a2,a3,-numb=7]

In this Erlang implementation, we don’t have built-in help text generation. You would need to implement this functionality yourself if required.

If you provide a flag that wasn’t specified in the parser, the program will print an error message and continue parsing the rest of the arguments.

$ erl -noshell -s command_line_flags main -wat -word=hello -s init stop
Unknown flag: wat
word: hello
numb: 42
fork: false
svar: bar
tail: []

This Erlang implementation provides similar functionality to the original example, with some differences due to the language’s characteristics and the simplicity of our custom parser.