Elixir - call a module function from the shell

skwidbreth picture skwidbreth · Apr 4, 2018 · Viewed 9.5k times · Source

In Elixir, is there a way to call a module function directly from the shell, without necessarily needing to start an iex -S mix session? Let me illustrate with a scenario:

As part of my Phoenix app, I've written a helper module that is to be run from an adjacent iex -S mix session. Here's a super-simplified version:

defmodule MyApp.Helper do
  # For the demo, these imports/aliases are not used - but they're there in real life.
  import Ecto.Query
  alias MyApp.Repo

  def start do
    {:ok, "Done"}
  end
end

If I start the session with iex -S mix and then run a function from the module, it all works fine:

$ iex -S mix
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Compiling 2 files (.ex)
Interactive Elixir (1.5.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> MyApp.Helper.start
{:ok, "Done"}

And then ctrl-c a to close the session.

However, if I try something like:

$ iex -S mix MyApp.Helper.start

That results in

Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Compiling 2 files (.ex)
** (Mix) The task "MyApp.Helper.start" could not be found

Alternately, I tried redefining my module as a custom mix task, as described here: https://elixirschool.com/en/lessons/basics/mix-tasks/#custom-mix-task

but that also failed, because my module depends on some imports/aliases like MyApp.Repo, and trying to execute the file with either mix helper or iex -S mix helper resulted in

** (ArgumentError) repo MyApp.Repo is not started, please ensure it is part of your supervision tree

If there's no way around this and the script can only be successfully executed from within a running iex -S mix, that's fine... but if there was a way to set things up so a one-liner could run from the shell to get this to execute as-needed, that'd be the bee's knees.

Answer

Dogbert picture Dogbert · Apr 4, 2018

You can use mix run with the -e argument for this:

$ mix run -e MyApp.Helper.start

or if you have arguments to pass to the function:

$ mix run -e "MyApp.Helper.start(:foo, :bar)"

From mix help run:

If there is a desire to execute a script within the current application or configure the application via command line flags, it is possible to do so by passing a script file or an eval expression to the command:

mix run my_app_script.exs arg1 arg2 arg3
mix run -e "MyApp.start" -- arg1 arg2 arg3