14

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.

1 Answer 1

20

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
Sign up to request clarification or add additional context in comments.

1 Comment

+1 — worth adding that if you want to run a one-liner outside a mix project you can pass --no-mix-exs as an argument; e.g: mix run --no-mix-exs -e 'IO.puts(:hello)'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.