当前位置: 首页 > article >正文

Elixir 工具篇

原理篇

学习一门新的语言之前,有必要先了解下它提供的工具。通过这些工具我们才能编译和运行代码,这俗话说的好,纸上得来终觉浅,绝知此事,它要躬行呐!实践永远是学习的不二法门。

相比于 erlang,elixir 提供的工具要更加现代化。安装完 elixir 之后,你会得到四个工具,它们都在 elixir 安装目录的 bin 目录下,分别是 elixirelixirciexmix 。打开看一下你就会发现它们其实都是脚本。没错,都是脚本!

在这里插入图片描述

这些文件中,不带扩展名的是 Linux 脚本, .bat Windows 命令行脚本, .ps1 是 Windows power shell 脚本。因为在 Windows 环境,我们以 bat 脚本为离说明,功能都是一样的。

从易到难,我们先看 mix.bat 。它的内容如下:

@if defined ELIXIR_CLI_ECHO (@echo on) else (@echo off)
call "%~dp0\elixir.bat" "%~dp0\mix" %*

第一行其实相当于 @echo off 用来关闭 bat 脚本的命令回显,属于写 bat 脚本的基操,只不过这里可以通过 ELIXIR_CLI_ECHO 环境变量开启命令回显。

第二行才是核心, %dp0 是命令所在目录,也就是 mix.bat 所在目录,所以它其实是在调用 elixir.bat 脚本,并且给了两个参数。实际上, iexelixirc 都只是在调用 elixir.bat 脚本而已,只不过大家传的参数不一样。

接下来我们来看看 iex.bat 的内容:

@echo off
setlocal
if /I ""%1""==""--help"" goto documentation
if /I ""%1""==""-h""     goto documentation
if /I ""%1""==""/h""     goto documentation
if    ""%1""==""/?""     goto documentation
goto run

:documentation
echo Usage: %~nx0 [options] [.exs file] [data]
echo.
echo The following options are exclusive to IEx:
echo.
echo   --dbg pry           Sets the backend for Kernel.dbg/2 to IEx.pry/0
echo   --dot-iex "FILE"    Evaluates FILE, line by line, to set up IEx' environment.
echo                       Defaults to evaluating .iex.exs or ~/.iex.exs, if any exists.
echo                       If FILE is empty, then no file will be loaded.
echo   --remsh NAME        Connects to a node using a remote shell
echo   --werl              Uses Erlang's Windows shell GUI (Windows only)
echo.
echo Set the IEX_WITH_WERL environment variable to always use werl.
echo It accepts all other options listed by "elixir --help".
goto end

:run
if defined IEX_WITH_WERL (set __ELIXIR_IEX_FLAGS=--werl) else (set __ELIXIR_IEX_FLAGS=)
call "%~dp0\elixir.bat" --no-halt --erl "-user elixir" +iex %__ELIXIR_IEX_FLAGS% %*
:end
endlocal

虽然内容变多了一些,但是真正有用的还是只有 :run:end 之间的两行, :documentation:run 之间的是命名帮助文档。仔细看 :run:end 之间的第二行代码就会发现,它实际上也是在调用 elixir.bat 脚本,但是这次参数比较多,注意倒数第二个参数 +iex %__ELIXIR_IEX_FLAGS%%__ELIXIR_IEX_FLAGS% 的值来自于上一行代码,根据 IEX_WITH_WERL 环境变量的设置与否,它要么是 --werl ,要么是空。

那么 --werl 参数是干嘛的呢?它其实和 erlang 有关,在 Windows 上安装 erlang 之后,我们有两种方式启动 erlang shell,一种是直接在命令行输入 erl 回车,一种是在开始菜单搜索 erlang。后一种打开的其实就是一个叫 werl.exe 的程序,这是 erlang 自带的交互式命令窗口。那么加了 --werl 参数之后,意思就是要打开 werl.exe 程序,而不是一个 Windows 命令行。其实这也很好理解,因为 elixir 程序最终还是编译成 beam 文件了,要运行 elixir 程序,我们就需要 erlang 虚拟机,还是回到 erlang 上了。后面我们会再次证实这一点。

如果你在开始菜单中搜索 elixir,也会找到一个程序,但实际上它只是一个快捷方式。右键选择“打开文件位置”,你会在 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Elixir 目录下看到一个叫 Elixir 的快捷方式,右键选择“属性”打开属性窗口,在“快捷方式”选项卡找到“目标”栏,可以看到它指向的程序其实就是 werl.exe 。当然这个快捷方式在后续版本中也被删除了,1.14还有,升级到1.17就没有了。

"D:\program\Erlang OTP\bin\werl.exe" -env ERL_LIBS "D:\program\Elixir\lib" -user Elixir.IEx.CLI -extra --no-halt

看到这里,熟悉 erlang 的话,应该会露出会心一笑。

好了,我们继续来看 elixirc.bat 脚本,它的内容如下:

@echo off
setlocal
set argc=0
for %%A in (%*) do (
  if /I "%%A"=="--help" goto documentation
  if /I "%%A"=="-h"     goto documentation
  if /I "%%A"=="/h"     goto documentation
  if    "%%A"=="/?"     goto documentation
  set /A argc+=1
)
if %argc%==0 goto documentation
goto run

:documentation
echo Usage: %~nx0 [elixir switches] [compiler switches] [.ex files]
echo.
echo   -h, --help                Prints this message and exits
echo   -o                        The directory to output compiled files
echo   -v, --version             Prints Elixir version and exits (standalone)
echo.
echo   --ignore-module-conflict  Does not emit warnings if a module was previously defined
echo   --no-debug-info           Does not attach debug info to compiled modules
echo   --no-docs                 Does not attach documentation to compiled modules
echo   --profile time            Profile the time to compile modules
echo   --verbose                 Prints compilation status
echo   --warnings-as-errors      Treats warnings as errors and returns non-zero exit status
echo.
echo ** Options given after -- are passed down to the executed code
echo ** Options can be passed to the Erlang runtime using ELIXIR_ERL_OPTIONS
echo ** Options can be passed to the Erlang compiler using ERL_COMPILER_OPTIONS
goto end

:run
call "%~dp0\elixir.bat" +elixirc %*

:end
endlocal

elixirc 是 elixir 的编译工具,它更简单,真正有用的只有一行,就是 :run:end 之间的那行,

上面都是命令的帮助文档。不出意外,它依然是在调用 elixir.bat 脚本。

最后让我们来看看重头戏 elixir.bat 脚本,它的内容如下,由于内容比较多我们省略了帮助文档部分。

@echo off

set ELIXIR_VERSION=1.17.3

if    ""%1""==""""                if ""%2""=="""" goto documentation
if /I ""%1""==""--help""          if ""%2""=="""" goto documentation
if /I ""%1""==""-h""              if ""%2""=="""" goto documentation
if /I ""%1""==""/h""              if ""%2""=="""" goto documentation
if    ""%1""==""/?""              if ""%2""=="""" goto documentation
if /I ""%1""==""--short-version"" if ""%2""=="""" goto shortversion
goto parseopts

:documentation
... ...
goto end

:shortversion
echo %ELIXIR_VERSION%
goto end

:parseopts
setlocal enabledelayedexpansion

rem Parameters for Erlang
set parsErlang=

rem Optional parameters before the "-extra" parameter
set beforeExtra=

rem Option which determines whether the loop is over
set endLoop=0

rem Designates the path to the current script
set SCRIPT_PATH=%~dp0

rem Designates the path to the ERTS system
set ERTS_BIN=
set ERTS_BIN=!ERTS_BIN!

rem Recursive loop called for each parameter that parses the cmd line parameters
:startloop
set "par=%~1"
if "!par!"=="" (
  rem skip if no parameter
  goto run
)
shift
set par="!par:"=\"!"
rem ******* EXECUTION OPTIONS **********************
if !par!=="--werl"   (set useWerl=1 && goto startloop)
if !par!=="+iex"     (set useIEx=1 && goto startloop)
if !par!=="+elixirc" (goto startloop)
rem ******* ELIXIR PARAMETERS **********************
if ""==!par:-e=!          (shift && goto startloop)
if ""==!par:--eval=!      (shift && goto startloop)
if ""==!par:--rpc-eval=!  (shift && shift && goto startloop)
if ""==!par:-r=!          (shift && goto startloop)
if ""==!par:-pr=!         (shift && goto startloop)
if ""==!par:-pa=!         (shift && goto startloop)
if ""==!par:-pz=!         (shift && goto startloop)
if ""==!par:-v=!          (goto startloop)
if ""==!par:--version=!   (goto startloop)
if ""==!par:--no-halt=!   (goto startloop)
if ""==!par:--remsh=!     (shift && goto startloop)
if ""==!par:--dot-iex=!   (shift && goto startloop)
if ""==!par:--dbg=!       (shift && goto startloop)
rem ******* ERLANG PARAMETERS **********************
if ""==!par:--boot=!                (set "parsErlang=!parsErlang! -boot "%~1"" && shift && goto startloop)
if ""==!par:--boot-var=!            (set "parsErlang=!parsErlang! -boot_var "%~1" "%~2"" && shift && shift && goto startloop)
if ""==!par:--cookie=!              (set "parsErlang=!parsErlang! -setcookie "%~1"" && shift && goto startloop)
if ""==!par:--hidden=!              (set "parsErlang=!parsErlang! -hidden" && goto startloop)
if ""==!par:--erl-config=!          (set "parsErlang=!parsErlang! -config "%~1"" && shift && goto startloop)
if ""==!par:--logger-otp-reports=!  (set "parsErlang=!parsErlang! -logger handle_otp_reports %1" && shift && goto startloop)
if ""==!par:--logger-sasl-reports=! (set "parsErlang=!parsErlang! -logger handle_sasl_reports %1" && shift && goto startloop)
if ""==!par:--name=!                (set "parsErlang=!parsErlang! -name "%~1"" && shift && goto startloop)
if ""==!par:--sname=!               (set "parsErlang=!parsErlang! -sname "%~1"" && shift && goto startloop)
if ""==!par:--vm-args=!             (set "parsErlang=!parsErlang! -args_file "%~1"" && shift && goto startloop)
if ""==!par:--erl=!                 (set "beforeExtra=!beforeExtra! %~1" && shift && goto startloop)
if ""==!par:--pipe-to=!             (echo --pipe-to : Option is not supported on Windows && goto end)

:run
setlocal disabledelayedexpansion
reg query HKCU\Console /v VirtualTerminalLevel 2>nul | findstr /e "0x1" >nul 2>nul
if %errorlevel% == 0 (
  set beforeExtra=-elixir ansi_enabled true %beforeExtra%
)
if not defined useIEx (
  set beforeExtra=-s elixir start_cli %beforeExtra%
)

set beforeExtra=-noshell -elixir_root "%SCRIPT_PATH%..\lib" -pa "%SCRIPT_PATH%..\lib\elixir\ebin" %beforeExtra%

if defined ELIXIR_CLI_DRY_RUN (
   if defined useWerl (
     echo start "" "%ERTS_BIN%werl.exe" %ext_libs% %ELIXIR_ERL_OPTIONS% %parsErlang% %beforeExtra% -extra %*
   ) else (
     echo "%ERTS_BIN%erl.exe" %ext_libs% %ELIXIR_ERL_OPTIONS% %parsErlang% %beforeExtra% -extra %*
   )
) else (
  if defined useWerl (
    start "" "%ERTS_BIN%werl.exe" %ext_libs% %ELIXIR_ERL_OPTIONS% %parsErlang% %beforeExtra% -extra %*
  ) else (
    "%ERTS_BIN%erl.exe" %ext_libs% %ELIXIR_ERL_OPTIONS% %parsErlang% %beforeExtra% -extra %*
  )
)
exit /B %ERRORLEVEL%
:end
endlocal

虽然内容比较长,但是能看出来三大部分:文档,参数处理和命令调用。文档就不说了,参数稍后再看,命令调用也是两行代码,要么调用 werl.exe 要么调用 erl.exe ,最终我们还是回到了 erlang。

:startloop:run 之间循环处理传递给脚本的每个参数, shift 指令会将参数列表整体左移一位。当然它也可以使用 shift \n 左移多位。此外 set par="!par:"=\"!" 会将参数中的 " 全部替换成 \" ,也就是会加个转义。

这里的逻辑也并不复杂,只是在做参数过滤和拼接。如果你感兴趣的话,可以配置一个 ELIXIR_CLI_DRY_RUN 环境变量,然后 elixir 就会打印出最终的命令而不会执行。比如我们可以打开一个 cmd 窗口,然后 set 一个临时环境变量来查看效果:

在这里插入图片描述

在运行 erl.exewerl.exe 时,通过 -pa 选项将 elixir 的库提前加载了,所以我们能使用 elixir 的库函数,熟悉 erlang 的朋友应该不会对此感到陌生。原理讲完了,接下来介绍下这些命令怎么使用。

使用篇

mix

首先我们看 mix 命令,它是 elixir 的工程管理工具,包括工程的新建,编译,测试,打包等,而且还可以由用户去实现自定义的命令。在命令行运行 mix help 可以查看 mix 的全部命令列表,每个人打印出来的命令列表可能会不太一样,比如我安装了 phoenix,所以会由 phx.xxx 等命令。我们来看几个比较常用的。

  • mix new app_name 创建应用
  • mix format 格式化代码
  • mix deps.get 获取依赖
  • mix test 运行测试
  • mix compile 编译工程
  • mix run 运行工程
  • mix escript.build 编译成“可执行”文件

这里重点对最后两个命令做一点说明。首先 mix run 运行工程的话必须是在“应用模式”下。所谓工程其实可以简单理解为根目录下有 mix.exs 文件。那么“应用模式”又是什么呢?

这里的“应用”就是 erlang 中 application 的概念,或者理解为一个 OTP 应用。在 elixir 中我们需要做两件事:

  1. mix.exs 文件中定义 application。

    def application do
      [mod: {MyApp, []}]
    end
    
  2. 实现 Application 行为以及它的 start/2 回调。

    defmodule MyApp do
      use Application
    
      def start(_type, _args) do
        children = []
        Supervisor.start_link(children, strategy: :one_for_one)
      end
    end
    

深入学习可以参考官方文档👉点击直达👈。

使用 mix run 运行应用时,应用运行起来后 mix 就退出了,有时候会来不及看到应用的结果,所以通常我们会加上 --no-halt 选项不让它退出: mix run --no-halt 。虽然 mix 本身默认就是执行的 mix run 任务,但是 mix --no-halt 是不行的。

除了运行应用 mix run 也能直接运行 elixir 表达式或 .exs 文件,如:

  • mix run -e "IO.puts(:hello)"
  • mix run hello.exs

-e--eval 的简短形式。 mix 毕竟是工程管理命令,它会去检查 mix.exs 文件的存在,如果不存在,上面的命令就会报错,这时我们需要加上 --no-mix-exs 选项跳过检查。

  • mix run -e "IO.puts(:hello)" --no-mix-exs
  • mix run hello.exs --no-mix-exs

完整的文档可以通过 mix help run 查看。 mix help task 可以用来查看 mix 任务的帮助文档。

Elixir 代码编译之后是 .beam 文件,需要加载到 erlang 虚拟机中才能运行,不能像 go 那样直接编译成可执行文件。erlang 通过 escript 机制也能实现类似的效果,将代码编译成一个文件,然后通过 escript 来运行。 mix escript.build 命令就是将工程编译成这样的”可执行”文件。

那么代价是什么呢?还是两件事情:

  1. 编写一个包含 main/1 函数的模块。

    defmodule My.CLI do
      def main(argv) do
        IO.puts(argv)
      end
    end
    
    
  2. mix.exs 的 project 配置中增加 escript 配置,配置 main_module 为包含 main/1 函数的模块。

    defmodule My.MixProject do
      use Mix.Project
    
      def project do
        [
          app: :my,
          escript: escript_config(),
          version: "0.1.0",
          elixir: "~> 1.14",
        ]
      end
    
      ...
    
      defp escript_config do
        [
          main_module: My.CLI
        ]
      end
    end
    

运行 mix escript.build 会编译出一个和工程同名的没有扩展名的文件,我们可以运行命令 escript xxx 来运行程序,命令行参数会通过 argv 传递给 main 函数。

当然在开发时,我们可能更常用的是使用 iex -S mix 在交互式命令行中来运行项目代码,如果你去查看 mix help 打印出来的命令列表就会发现,列表的最后一条命令就是 iex -S mix

iex

iex 是 elixir 的交互式解释器,我们可以在命令行直接输入 iex 进入,然后就可以输入 elixir 表达式查看结果了,在 iex 中可以使用 v 来引用上一个表达式的结果,或者 v() 也是可以的。

在 elixir 工程目录下,可以使用 iex -S mix 在进入 iex 之前编译项目并将其加载进虚拟机。如果是单独的 .ex.exs 文件,也可以通过 iex xxx.exiex xxx.exs 将其加载进虚拟机。

在进入 iex 之后,也可以通过 c "xxx.ex"c "xxx.exs" 将代码加载进虚拟机。

在代码更改之后,我们有两种方式去重新编译和加载代码:

  • recomplie 它会重新编译和加载整个 mix 工程。
  • r ModuleNamer [Module1, Module2] 它只会重新加载指定的模块。

iex 还有一些有用的函数,当然这些都是 erlang 提供的,谁叫他站在了巨人的肩膀上呢。

  • pwd 打印当前目录
  • cd 切换目录
  • ls 列出当前目录下的文件和目录

如果要在 iex 中查看帮助的话,可以使用 h(xxx)

elixirc

elixirc 是 elixir 的编译工具,用来编译 .ex 文件,生成 .beam 文件。但实际上它也能把 .exs 文件也能编译成 .beam 文件,如果 .exs 文件中有模块定义的话。如果 .exs 文件中都是一些命令,如 IO.puts("xxx") ,那么 elixirc xxx.exs 会直接运行整个脚本,不会编译出 .beam 文件。

elixir

直接使用 elixir 命令的话一般用来求值 elixir 表达式或者运行 .exs 脚本。

  • elixir -e "IO.puts(:hello)"
  • elixir xxx.exs

Elixir 源代码文件有两种扩展名, .ex 是源码文件,需要编译后运行, .exs 是脚本文件,可以直接解释运行。

总的来说,我们用到 mixiex 的概率会大很多, elixirc 很少会直接使用它来编译, elixir 会在运行 elixir 脚本的情况下用到。


http://www.kler.cn/news/355943.html

相关文章:

  • Flink PostgreSQL CDC源码解读:深入理解数据流同步
  • Android一代整体壳简易实现和踩坑记录
  • Linux Ubuntu dbus CAPI ---- #include<dbus.h>出现“无法打开源文件dbus/xxx.h“的问题
  • 数据结构-5.8.由遍历序列构造二叉树
  • Python进阶--海龟绘图turtle库
  • Dungeon Crawler Grid Controller 地牢移动控制器
  • iOS模拟弱网步骤
  • 法律文书审查专项使用大模型实现
  • 在docker的容器内如何查看Ubuntu系统版本
  • Linux零基础教程学习(黑马)
  • Netty
  • MatrixOne助力江铜集团打造炉前智慧作业AIoT大数据系统
  • 分布式介绍
  • 框架一 Mybatis Spring SpringMVC(东西居多 后边的没怎么处理)
  • 05 django管理系统 - 部门管理 - 修改部门
  • Linux :at crontab简述
  • 【论文解读系列】EdgeNAT: 高效边缘检测的 Transformer
  • 猫鼠游戏: KaijiK病毒入侵溯源分析
  • (一)Java1.8核心包rt.jar——java.lang.annotation
  • Web APIs - 第2天笔记(黑马笔记)