1. 引言:为什么选择Elixir?

"用Erlang虚拟机跑命令行工具?这听起来像用航空母舰钓鱼啊!"——这是很多人初次接触Elixir时的疑惑。但当你真正了解Elixir的并发模型、模式匹配和宏系统后,就会发现它其实是构建命令行工具的绝佳选择。举个例子,GitHub的Semantic代码分析工具就是用Elixir开发的,每天处理数百万次代码解析请求。

2. 环境准备

(技术栈:Elixir 1.14+)

# 安装最新Elixir(以macOS为例)
brew update
brew install elixir

# 验证安装
elixir -v
# 输出示例:Erlang/OTP 25 [erts-13.0.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]
# Elixir 1.14.3 (compiled with Erlang/OTP 25)

3. 构建第一个CLI应用

3.1 创建Mix项目

mix new magic_cli --sup
cd magic_cli

3.2 基础命令实现

# lib/magic_cli.ex
defmodule MagicCLI do
  @moduledoc """
  魔法命令行入口模块
  """
  
  def main(args) do
    args
    |> parse_args
    |> process
  end

  defp parse_args(args) do
    {opts, _} = OptionParser.parse!(args,
      strict: [help: :boolean, version: :boolean],
      aliases: [h: :help, v: :version]
    )
    
    opts
  end

  defp process([help: true]), do: show_help()
  defp process([version: true]), do: show_version()
  defp process(_), do: execute_default()

  defp show_help do
    IO.puts """
    使用说明:
      --help    显示帮助信息
      --version 显示版本信息
    """
  end

  defp show_version do
    IO.puts "魔法命令行 v1.0.0"
  end

  defp execute_default do
    IO.puts "正在施展默认魔法..."
    # 这里可以添加业务逻辑
  end
end

4. 进阶功能实现

4.1 子命令系统

# 修改lib/magic_cli.ex
defmodule MagicCLI do
  # ...原有代码...

  defp parse_args(args) do
    {opts, args} = OptionParser.parse!(args,
      strict: [help: :boolean, version: :boolean],
      aliases: [h: :help, v: :version],
      switches: [command: :string]
    )

    {opts, args}
  end

  defp process({[command: "encrypt"], file_args}) do
    # 加密处理逻辑
    file_args |> Enum.each(&encrypt_file/1)
  end

  defp process({[command: "decrypt"], file_args}) do
    # 解密处理逻辑
    file_args |> Enum.each(&decrypt_file/1)
  end

  defp encrypt_file(file) do
    IO.puts "正在加密 #{file}..."
    # 实际加密实现
  end

  defp decrypt_file(file) do
    IO.puts "正在解密 #{file}..."
    # 实际解密实现
  end
end

4.2 彩色输出

# 添加依赖到mix.exs
defp deps do
  [
    {:io_ansi_table, "~> 0.1.0"}
  ]
end

# 使用示例
defmodule MagicCLI.Formatter do
  import IO.ANSI
  
  def success(msg) do
    IO.puts [green(), "✓ ", reset(), msg]
  end

  def error(msg) do
    IO.puts [red(), "✗ ", reset(), msg]
  end

  def progress_bar(current, total) do
    width = 40
    percent = current / total * 100
    filled = round(width * current / total)
    
    bar = [
      blue_background(),
      String.duplicate(" ", filled),
      reset(),
      String.duplicate(" ", width - filled),
      " #{round(percent)}%"
    ]
    
    IO.write(["\r", bar])
  end
end

5. 跨平台打包

5.1 使用escript

# 修改mix.exs
def project do
  [
    app: :magic_cli,
    escript: [main_module: MagicCLI],
    version: "1.0.0",
    # ...其他配置...
  ]
end

# 打包命令
mix escript.build

5.2 Burrito打包工具(跨平台利器)

# 添加依赖
{:burrito, github: "burrito-elixir/burrito"}

# 创建rel/envs.exs
import Config

config :magic_cli,
  burrito: [
    targets: [
      macos: [os: :darwin, cpu: :x86_64],
      linux: [os: :linux, cpu: :x86_64],
      windows: [os: :windows, cpu: :x86_64]
    ]
  ]

# 打包命令
MIX_ENV=prod mix release

6. 关联技术深入

6.1 进程监控系统

defmodule MagicCLI.Watcher do
  use GenServer

  def start_link(_) do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def init(_) do
    Process.flag(:trap_exit, true)
    {:ok, %{}}
  end

  def handle_info({:EXIT, pid, reason}, state) do
    IO.puts "进程 #{inspect pid} 异常退出,原因:#{reason}"
    {:noreply, state}
  end
end

6.2 热更新实现

defmodule MagicCLI.LiveUpdate do
  def reload do
    # 重新加载所有模块
    Mix.Task.run("compile")
    
    # 清除旧版本代码
    :code.purge(MagicCLI)
    :code.delete(MagicCLI)
    
    # 加载新代码
    {:module, _} = Code.ensure_compiled(MagicCLI)
  end
end

7. 应用场景分析

  1. DevOps工具链:利用Elixir的并发特性处理CI/CD流水线
  2. 数据处理工具:快速处理GB级日志文件
  3. IoT设备管理:结合Nerves框架实现嵌入式CLI
  4. 区块链节点工具:高效处理区块链交易数据

8. 技术优缺点对比

优势:

  • 毫秒级热更新(无需重启进程)
  • 天然的容错机制(Supervisor树)
  • 卓越的并发处理能力(OTP加持)
  • 模式匹配简化参数解析

劣势:

  • 冷启动时间较长(VM启动耗时)
  • 二进制体积较大(相比Go/Rust)
  • 生态相对年轻(部分库不够成熟)

9. 注意事项

  1. 参数安全处理:使用OptionParser的strict模式
OptionParser.parse(args, strict: [valid_flag: :boolean])
  1. 跨平台路径处理
Path.join(["parent", "child.txt"]) # 自动适应系统分隔符
  1. 版本兼容性:明确指定Erlang/OTP版本
  2. 内存管理:避免在长期运行的CLI中积累状态

10. 总结与展望

通过本教程,我们完成了从基础CLI到支持热更新、跨平台打包的全功能命令行工具开发。Elixir的并发模型让我们的工具可以轻松处理后台任务,而模式匹配则让复杂的参数解析变得优雅简洁。未来可以考虑集成WebAssembly运行时,或者结合Phoenix框架开发可视化CLI界面。