LoginSignup
21
2

More than 1 year has passed since last update.

Elixirコードから外部コマンドを直接叩けると便利な場合があります。

System.cmd/3 :flag_jp:

引数

  1. PATHで使用可能な実行可能なファイル名、またはコマンドの絶対パス
  2. コマンドがそのまま引数として受け取れる文字列のリスト

戻り値

早速IExを開きます。

iex

コマンドに渡す引数がある場合

echo "元氣"をElixirから叩いてみます。

iex> System.cmd "echo", ["元氣"]
{"元氣\n", 0}

終了ステータスが0なので正常です。

ls -1 -aをElixirから叩いてみます。

コマンドに渡す引数が複数の場合は各引数を別々の文字列として引数リストに入れます。

iex> System.cmd "ls", ["-1", "-a"]
{".\n..\n.elixir_ls\n.formatter.exs\n.git\n.gitignore\nREADME.md\nlib\nmix.exs\nmix.lock\ntest\n",
 0}

コマンドに渡す引数がない場合

lsをElixirから叩いてみます。

コマンドに渡す引数がない場合にも引数リストが必要なようです。

iex> System.cmd "ls"
** (UndefinedFunctionError) function System.cmd/1 is undefined or private. Did you mean:

      * cmd/2
      * cmd/3

    (elixir 1.14.0) System.cmd("ls")
    iex:19: (file)

iex> System.cmd "ls", []
{"\n", 0}

ちょっと使い勝手が悪いですが、コマンドインジェクション攻撃を防ぐために意図的に引数が指定された通りにしか解釈されないようにしているようです。

信頼できるコマンドをもっと自由にパイプリダイレクトなどを使って実行したい場合はSystem.shell/2があります。

System.shell/2 :flag_jp:

ls | sortをElixirから叩いてみます。

iex> System.shell("ls | sort")
{"README.md\nlib\nmix.exs\nmix.lock\ntest\n", 0}

System.find_executable/1 :flag_jp:

コマンドを叩く前にそのコマンドが存在するのか確認したい場合はSystem.find_executable/1が便利。

iex> System.find_executable("ls")
"/bin/ls"

iex> System.find_executable("/bin/echo")
"/bin/echo"

iex> System.find_executable("闘魂")
nil

System.cmd/3の中でもコマンドの存在を確認してくれていますが、存在しない場合は:enoentエラーになります。

iex> System.cmd "foo", []
** (ErlangError) Erlang error: :enoent
    (elixir 1.14.0) lib/system.ex:1053: System.cmd("foo", [], [])
    iex:33: (file)

Port :flag_jp:

System.cmd/3では、PortというElixirの外の世界と対話するための仕組みが利用されています。

ですので、Elixirの提供する仕組みで満足できない場合は、Portを使って自分で実装することも可能だと思います。知らんけど。

Systemモジュールのソースコードを見てみる

Systemモジュールのソースコードを見てみると面白かったです。

  • いろんなErlangの関数が活用されている
  • パターンマッチでオプションを解析

ご参考までに

21
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
2