05月30, 2024

Amber---下一个 shell 脚本何必是 shell script

Linux shell script 是我最喜欢的脚本之一,历史悠久,底蕴深厚,shell script 几乎是内核的一部分,托 POSIX 标准的福,你可以在任何 Linux 系统中使用它。但是编写 shell script 体验并不太好,各种奇怪的语法、没有类型检查、孱弱的数组支持,有时候排查很久的问题,仅仅是错误的使用引号、或者多了一个空格。

我很喜欢造轮子,曾打算写一个高级语言,可以编译成 shell script 执行,即使是一些简单的语法糖,也能很大程度提升 shell script 用户的幸福感。前几天发现一个项目 Amber 已经做了这些工作,试用了下还不错,语法类似 Ecmas 或者 js,支持 Text、Num、Bool、Null 和 [] 数组,可以直接编译成 shell script,直接拿到任何 shell 中执行,没有任何移植性困扰。

先睹为快

参考官网的安装指导,只需要一行命令:

curl -s "https://raw.githubusercontent.com/Ph0enixKM/AmberNative/master/setup/install.sh" | bash

请确保你的系统已安装了 curl、bc, 安装以后就可以使用 amber 命令。 下面是一个简单的例子:

let fruits = ["apple", "banana", "grape"]

fun show_opt(fruits) {
    loop index, f in fruits {
        echo "{index}: {f}"
    }
}

show_opt(fruits)

将上面的代码保存为 test1.ab, 执行它

~/test $ amber test1.ab
0: apple
1: banana
2: grape

只要你有任何一种变成经验,很容易明白 amber 的基本语法。

上面的 amber 脚本等效的 shell 如何呢?我们可以将 amber 脚本编译为 shell 脚本。

amber test1.ab test1.sh

相比直接执行 amber 脚本,只需要增加一个参数指定编译输出的脚本文件名即可。test1.sh 看起来这样:

__AMBER_ARRAY_0=("apple" "banana" "grape");
__0_fruits=("${__AMBER_ARRAY_0[@]}");
function show_opt__0_v0 {
    local fruits=("${!1}")
    index=0;
for f in "${fruits[@]}"
do
        echo "${index}: ${f}"
        let index=${index}+1
done
};
show_opt__0_v0 __0_fruits[@];
__AMBER_FUN_show_opt0_v0__9=${__AMBER_FUN_show_opt0_v0};
echo ${__AMBER_FUN_show_opt0_v0__9} > /dev/null 2>&1

相比之下,amber 脚本真的是相当人性化。

执行命令

shell 最强大的能力在于可以方便的调用已安装的命令。amber 也具备这样的能力,而且提供了良好的异常处理能力。

基本语法是

$your cmd$ failed { exception handler }

两个 $ 之间可以是任何有效的 shell 命令,failed 是 amber 的关键字,表示指令的异常处理。

为了正常使用异常机制,异常所在代码要么位于 main block,要么在函数中。amber 可以指定 main 作为脚本入口。

main {
    let file_name = "1.txt"
    let cmd = "cat"
    let file_content = ${cmd} {file_name}$ failed {
        echo "{file_name} not exist"
        fail
    }

    echo file_content
    echo "done"
}

这段代码的尝试获取 1.txt 的内容,如果这个文件不存在就报错退出,否则打印文件内容。

指令可以是任何字符串,比如例子中的命令由 cmdfile_name 变量拼接生成。 cmd 的标准输出可以直接赋值给变量,获取指令输出非常方便。当使用 cmd 调用指令时,必须指定异常处理,可以通过 failed 关键字指明之后的代码块用作异常处理。fail 关键字用于抛出异常。

还有一种简化的一场处理方式,上面代码可以稍作调整:

    let file_content = ${cmd} {file_name}$?

相当于

    let file_content = ${cmd} {file_name}$ failed {
        fail status
    }

我很喜欢 ? 的设计,表示指令是不可靠的,出错时就停止。二者的区别是简写方式无法指定报错信息。

总结

目前我已用 amber 作为 Linux 下编写脚本的首选工具,感觉好用。好工具,好生活,拯救脱发。

本文链接:http://www.thinkinpython.com/post/amber_script.html

-- EOF --