Writing a CLI app in Elixir

Samar Acharya
Brightergy Engineering
3 min readJul 26, 2016

The journey with Elixir has been pleasant so far and with the power of concurrency of Erlang (which we’re yet to leverage to the fullest) and sweet features of Elixir like pattern matching, guards and extensibility, we are very happy with the choice. So far, we had been mostly writing Phoenix based frontend and backend APIs and couple of client SDKs and recently, I got an opportunity to write a command line tool for copying data from one InfluxDB to another InfluxDB database. While I could have written it in a variety of other languages, this was a perfect opportunity to write my first command line application in Elixir.

Erlang supports short running programs via the escript tool. All you have to do is write your application the way Erlang scripting support expects. Also, note that you only need to have Erlang installed for escript to run even though you write an escript in Elixir as Elixir is embedded as part of the escript. We’ll dive into writing a CLI tool soon but lets first dissect the executable created by escript when you run a mix escript.build.

head -n3 influx_copy 
#! /usr/bin/env escript
%%
%%! -escript main influx_copy_escript

As you can see above, the executable created is simple and has a shebang line just like bash or python scripts. The second line consists of an optional directive to the Emacs editor which causes Emacs to enter the major mode for editing Erlang source files. The escripts built via Elixir do not contain the directive as seen above. The third line (or second if the second directive line is not present) specifies any arguments to be passed to the emulator. In the output above, we are overriding the default behavior of escript which is to invoke main/1 function in the module with the same name as the basename of the escript. Don’t worry! This is all handled by Elixir so all you have to specify is your main_module in your mix.exs file.

After the headers, the rest of the escript can either contain the plain Erlang code or precompiled beam code which is how it works with Elixir. If you look at the output executable, it will consist of all the beam code in a single executable. And, since all the necessary dependencies are compiled and included, we don’t explicitly need Elixir to be installed for escripts written in Elixir.

Now, lets quickly dive into the process of writing escript in Elixir. You can check out the influx-copy commits which I’ve tried as much to code in small incremental steps.

mix new influx-copy — module InfluxCopy — app influx_copy
cd influx-copy
mix test

The influx-copy tool is a simple generic tool that reads from one InfluxDB source and writes to another destination with ability to select fields and transform values of particular tags. The use case was that we had to copy some data from staging to production for one of our real-time monitors but their tag values were different in different environments.

The Erlang escript expects the main module to have a main/1 function which is what is invoked when the escript is run.

Update your mix.exs file to contain the following information

def project do
[app: :influx_copy,
version: “0.1.0”,
elixir: “~> 1.3”,
escript: escript,
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps()]
end
def escript, do: [main_module: InfluxCopy]

As you can see above, whatever main_module you specify is supposed to have the main/1 function defined.

defmodule InfluxCopy do
def main(args \\ []) do
{opts, _, _} = OptionParser.parse(args,
switches: [start: :integer, end: :integer, src: :string, dest: :string, update_tags: :string],
aliases: [s: :start, e: :end, S: :src, d: :dest, u: :update_tags]
)
IO.puts “Not implemented yet”
end
end

Now, your simple CLI app is ready to be compiled and used although it does not do anything.

$ mix escript.build
Generated influx_copy app
Generated escript influx_copy with MIX_ENV=dev
$ ./influx_copy
Not implemented yet

Elixir has a nice module called OptionParser that provides helpful functions to parse command line arguments which is quite useful while writing command line applications in Elixir. The influx-copy script does not yet include a helpful help message but it should be fairly simple to put.

I’d imagine adding an extra switch help and then based on truthiness of this argument, you can show help message instead.

If you are interested in the script I wrote and how I proceeded further, you can check out influx-copy.

--

--