tcoopman
Run a shell command with a pipe operator
I’m trying to run a shell command with a pipe operator in it. For example “sleep 0.1 | echo foo”. At first I tried to do this with System.cmd:
System.cmd("sleep", ["0.1", "|", "echo", "foo"])
But this results in /usr/bin/sleep: invalid time interval '|', invalid time interval 'echo'....
So next I tried to use :os.cmd:
:os.cmd("sleep 0.1 | echo foo")
But whatever I do to execute this I get:
:os.cmd("sleep 0.1 | echo foo")
** (FunctionClauseError) no function clause matching in :os.validate/1
The following arguments were given to :os.validate/1:
# 1
"sleep 0.1 | echo foo"
(kernel) os.erl:281: :os.validate/1
(kernel) os.erl:237: :os.cmd/1
Looking into the code of :os (https://github.com/erlang/otp/blob/master/lib/kernel/src/os.erl) is see that :os.validate expects atoms? But in the erlang docs there are no atoms used (http://erlang.org/doc/man/os.html)
Marked As Solved
tcoopman
I’ve just been able to solve my own problem.
If I do: os.cmd(:"sleep 0.1 | echo foo") it works.
So I need to pass an atom to os.cmd.
Also Liked
alco
I made a mistake in the invocation. To evaluate a string in sh, the -c has to be passed first:
System.cmd("sh", ["-c", "sleep 0.1"])
idi527
Or :os.cmd('sleep 0.1 | echo foo').
iex(1)> :os.cmd('sleep 0.1 | echo foo')
'foo\n'
It works with atoms and character lists. Doesn’t seem to work with binaries.
I would try to avoid creating new atoms for each command you might want to run since they are not garbage collected.
benwilson512
System.cmd does not run a shell, so pipes, redirects, and so on aren’t supported. System.cmd is basically the closest thing that Elixir has to fork.
:os.cmd is definitely handy, but take great care if any of the stuff in your command comes from users (like filenames). You risk letting users run arbitrary programs.
alco
It is strange that :os.cmd accepts an atom for the command name, looks like a historical accident.
In general, functions from the Erlang standard library take character lists where in Elixir you would use a string. To make it more confusing, the Erlang type for such lists is called string(). I recommend taking a look at this cheat sheet if you still feel confused.
Going back to your problem, Ben has already done a good job explaining the difference between Elixir’s System.cmd and Erlang’s :os.cmd. In the source code for :os.cmd you can see it actually spawns a shell appropriate for the current OS and evaluates the string you’ve passed to the function in that shell.
Replicating this behaviour for your particular example using System.cmd is easy:
System.cmd("sh", ["sleep 0.1 | echo foo"])
OvermindDL1
Not entirely. A lot of the ‘base’ functions get the string value of atoms, it makes it cheap and efficient to pass around something like sed instead of having to do "sed" in the original erlang (plus it’s fantastic since atoms in erlang can have @ in them without quoting too!). Calling other programs was generally used to access their stdin/stdout, not for argument parsing and ‘doing’ other stuff. ![]()
(Do note, the sed and "sed" above in erlang, in elixir would be :sed and 'sed' respectively.)







