17

At a very early stage of learning the language, and working through the ElixirSips series of videos. I keep hitting stuff that's been slightly obseleted by language changes. Trying to wrap my head around this, and Google/Github issue trackers/SO trawling is getting me nowhere useful. I have this very basic module, which should just run a shell command:

defmodule QuickieSynth.Sound do
  def command(note) do
    "play -qn synth 2 pluck #{note}"
  end

  def play(note) do
    note |> command |> System.cmd
  end
end

However, when this file is compiled and the tests run, I get an argument error; fair enough - System.cmd/1 seems to no longer be part of the standard lib.

System.cmd/3 is in the standard lib, and reading the docs indicated the options are, well, optional. So I pass empty args note |> command |> System.cmd([]), and what I get back is erlang: :enoent: again after reading the docs a bit more carefully, fair enough.

So I try to use Erlang's :os.cmd/1, so note |> command |> :os.cmd, and I get (FunctionClauseError) no function clause matching in :os.validate/1. And I am now stuck.

DanCouper
  • 850
  • 6
  • 15
  • 2
    I write another function to make that tutorial work on recent versions of Elixir: `def run(full_cmd) do [cmd| args] = String.split(full_cmd, " ") System.cmd(cmd, args) end` – hayesgm Apr 29 '15 at 01:46

1 Answers1

36

System.cmd/3 seems to accept the arguments to the command as a list and is not happy when you try to sneak in arguments in the command name. For example System.cmd("ls", ["-al"]) works, while System.cmd("ls -al", []) does not. So in your case you'll probably need something like:

System.cmd("play", ["-qn", "synth", "2", "pluck", note])

What in fact happens underneath is System.cmd/3 calls :os.find_executable/1 with its first argument, which works just fine for something like ls but returns false for ls -al.

If you decide to go the pure erlang route, then the call expects a char list instead of a binary, so you need something like the following:

"ls -al" |> String.to_char_list |> :os.cmd
Paweł Obrok
  • 22,568
  • 8
  • 74
  • 70
  • 1
    Ah, that makes complete sense. I'll want to build in options anyway, so it was just that first basic step that was tripping me up; I can't pipe it in the way I was trying to. BTW, any idea why the Erlang call won't work (:os.cmd)? – DanCouper Dec 29 '14 at 13:20
  • 1
    Added that to the answer – Paweł Obrok Dec 29 '14 at 13:38