使用旧的 Rust 2015

1.0.5 2017年7月15日
1.0.3 2017年6月17日
1.0.0 2017年3月12日
0.1.7 2017年2月14日
0.0.4 2015年12月17日

19#ion 中排名

每月下载 22

自定义许可

390KB
8K SLoC

Ion Shell

Build Status MIT licensed LOC

Ion 是一个完全用 Rust 编写的现代系统 shell,具有简单(且强大)的语法,并提供了超过 Dash 的性能。虽然它作为 RedoxOS 的默认 shell 与 RedoxOS 一起开发,但它也同等支持 UNIX 平台(Linux/Mac/BSDs),在该平台上它被开发和测试。Windows 支持也可以轻松获得,但我们目前没有使用 Windows 的开发者。Ion 的设计受到许多其他成功 shell 的影响,这可以从其从 Bash、Fish 和 Oil 中借鉴的想法中看出;同时,它还提供了一些独特想法。它仍在开发中,但大部分核心功能已完成。它目前也比 Dash 快得多,尽管它包含许多更多功能和能力,使其成为迄今为止最快的系统 shell。最后,由于它是用 Rust 编写的,我们可以保证我们的代码库提供的内存安全性比 Bash、Dash、Zsh、Fish 以及用不安全语言编写的其他 shell 高得多。这意味着不会出现类似于 shellshock 的漏洞。

Ion 的目标

Ion 的语法和功能决策基于三个特定的衡量标准:"功能是否有用,使用是否简单,以及其实施是否易于解析和执行?"。语言应易于解析,没有歧义的空间,并尽可能以零成本的方式实现。此外,我们认为作为一个基于字符串的语言,shell 也应该拥有第一级的字符串操作能力,以便消除对外部工具的需求。在编写 Ion 脚本时,不需要像以前那样经常使用 awk 命令,因为许多基本用法已集成到 Ion 的语法中,使其易于使用和学习。

Ion 不是 POSIX

虽然Ion的语法受到了POSIX shell语法的很大影响,但它提供了一些关键的特性和差异化功能,这些在POSIX shell中是找不到的。它们之间的相似性仅仅是因为POSIX语法已经包含了一些不错的想法,但也伴随着许多不良的设计决策,这导致了不灵活性。因此,我们在保留这些好想法的基础上,实施了更好的想法,并作为对不良部分的替代。因此,虽然语法看起来很熟悉,但它并不符合POSIX标准,也永远不会符合。

例如,我们保留了许多相同的基本功能,例如字符串($string)和返回字符串的过程展开($(command args...)),但我们还实现了对一等数组的支持(@array*) 和基于数组的进程展开(@(command args..)),而不是简单地组合字符串变量,并利用这两种类型之间的区别来实现方法($join(array)@split(string))和切片($string[..5]@array[..5])。此外,我们还实现了更好的重定向/管道stderr的语法(^>^|),以及stderr和stdout(&>&|);同时删除了do关键字,使用end关键字来结束一个块。

特性

以下是对Ion已实现或计划在未来实现的功能的概述。如果您有希望添加到该列表的功能想法,欢迎您提交一个问题来描述该功能及其用途/为什么您认为它有用。

  • 展开
    • 字符串展开
    • 数组展开
    • 通配符展开
    • 大括号展开
      • 范围
      • 排列
      • 嵌套大括号
    • 进程展开
      • 基于字符串的命令替换($()
      • 基于数组的命令替换(@()
    • 算术展开
  • 流程控制
    • For循环
    • Foreach循环
    • While循环
    • If条件
    • 匹配语句
  • 函数
    • 可选类型化的函数参数
  • 脚本执行
    • 使用@args数组处理参数
  • 可变变量
    • 别名
    • 字符串($variable
      • 多变量赋值
      • 可选类型化的变量赋值
      • 基于图形的字符串切片
      • 字符串方法($join(array, ', ')
        • $join(array)
        • $len(string)
        • $len_bytes(string)
    • 数组(@array
      • 数组表达式([]
      • 数组切片
      • 数组方法(@split(var, ' ')
        • @split(string)
        • @len(array)
    • HashMaps
  • 行编辑器(由Liner提供)
    • 多行注释和命令
    • 多行编辑
    • 自动补全
    • 自动建议
    • 历史建议
    • vi和emacs快捷键(set -o (vi|emacs)
    • 语法高亮显示
  • 隐式cd
  • 信号处理
  • &&||条件
  • 重定向Stdout / Stderr
  • 重定向Stdout & Stderr
  • 管道内置函数
  • 后台作业
  • 管道函数
  • 重定向函数
  • 后台作业控制
  • XDG App Dirs
  • 插件支持
    • 内置函数
    • 提示符
    • 语法

Shell语法

隐式目录更改

类似于Friendly Interactive Shell,Ion也支持在给定路径时隐式执行cd命令,只要该路径以.///~开始,或者以/结束。这将会调用内部内置的cd命令,并将该路径作为参数。

~/Documents # cd ~/Documents
..          # cd ..
.config     # cd .config
examples/   # cd examples/

作业控制

释放进程

Ion提供了一个disown命令,支持以下选项:

  • -r:从后台进程列表中删除所有正在运行的任务。
  • -h:指定当shell接收到SIGHUP信号时,每个任务将不会接收该信号。
  • -a:如果没有提供任务ID,则从后台进程列表中删除所有任务。

与Bash不同,作业参数是它们指定的作业ID。

前台和后台任务

此区域仍在开发中。当使用Ctrl+Z信号停止前台任务时,该进程将被添加到后台进程列表作为停止的作业。当提供的命令以&运算符结束,这将指定将任务作为运行中的作业在后台运行。要恢复停止的作业,执行bg <作业ID>命令将向指定的作业ID发送SIGCONT信号,从而恢复作业。类似地,fg命令也会这样做,但还会将任务设置为前台进程。

退出shell

exit命令将退出shell,并向所有仍然活动的后台任务发送SIGTERM信号。

挂起shell

虽然shell忽略SIGTSTP信号,但您可以通过执行suspend命令强制挂起shell,该命令通过SIGSTOP信号强制停止shell。

定义变量

使用let关键字在shell中创建局部变量。export关键字执行类似操作,只是将变量作为全局环境变量设置,操作系统可以访问。

let git_branch = $(git rev-parse --abbrev-ref HEAD ^> /dev/null)

也可以一次性分配多个变量或交换变量。

let a b = 1 2
let a b = [1 2]
let a b = [$b $a]

如果命令在没有任何参数的情况下执行,它将简单地列出所有可用的变量。

使用变量

可以使用$符号调用变量,后面的值可以是局部或全局值。还可以使用大括号语法可选地定义变量,这对于需要将值与不终止变量解析的字符结合使用的情况很有用。

let A = one
let B = two
echo $A:$B
echo ${A}s and ${B}s

从变量中提取子字符串

Ion支持使用与数组相同的切片语法通过图形符号分割提供的字符串。

$ let string = "one two three"
$ echo $string[0]
o
$ echo $string[..3]
one
$ echo $string[4..7]
two
$ echo $string[8..]
three

删除变量

要从shell中删除一个值,可以使用drop关键字。

drop git_branch

变量算术

let命令还支持基本的算术运算。

let a = 1
echo $a
let a += 4
echo $a
let a *= 10
echo $a
let a /= 2
echo $a
let a -= 5
echo $a

导出

export命令与let命令类似,但它不是定义局部变量,而是定义全局变量,其他进程可以访问。

export PATH = ~/.cargo/bin:${PATH}

导出算术

export命令还支持基本的算术运算。

export a = 1
echo $a
export a += 4
echo $a
export a *= 10
echo $a
export a /= 2
echo $a
export a -= 5
echo $a

别名

alias命令用于为运行其他命令设置别名。最常用的alias用法是缩短运行命令及其特定参数所需的按键次数,以及将命令重命名为更熟悉的名字。

alias ls = 'exa'

如果没有提供任何参数执行命令,它将简单地列出所有可用的别名。

unalias命令执行与alias相反的操作,即从存在中删除值。

unalias ls

大括号展开

大括号展开用于创建给定输入的排列。除了简单的排列之外,Ion还支持大括号范围和嵌套分支。

echo abc{3..1}def{1..3,a..c}
echo ghi{one{a,b,c},two{d,e,f}}

定义数组

当提供的表达式评估为一个值向量时,可以使用let关键字创建数组。

数组语法

创建值数组的语法是将值用[]括起来。括号内的语法将被评估为一个扁平映射的向量,因此结果可以存储为数组。

let array = [ one two 'three four' ]

数组的一个特定用途是设置命令参数

let lsflags = [ -l -a ]
ls @lsflags

复制数组

请注意,数组必须始终显式使用[]创建。

let array = [ 1 2 3 ]
let copy_of_array = [ @array ]
let string = @array

当数组变量自身传递时,它将被强制转换为字符串。

花括号可以创建数组

花括号展开实际上在底层创建了一个值向量,因此可以用来创建数组。

let braced_array = [ ]{down,up}vote ]

基于数组的命令替换

与标准命令替换语法创建单个字符串不同,这个变体将创建一个由空格分隔的值向量,该向量来自命令的输出。

let word_split_process = [ @(echo one two three) ]

使用数组

可以使用@符号调用数组,它与变量语法相同

echo @braced_array
echo @{braced_array}

当提供索引或索引范围时,数组也可以被切片

按索引切片

按索引切片将从数组中提取一个字符串

let array = [ 1 2 3 ]
echo @array[0]
echo @array[1]
echo @array[2]

echo [ 1 2 3 ][0]
echo [ 1 2 3 ][1]
echo [ 1 2 3 ][2]

echo @(echo 1 2 3)[0]
echo @(echo 1 2 3)[1]
echo @(echo 1 2 3)[2]

按范围切片

按范围切片将取数组的一个子集作为新数组

let array = [ 1 2 3 4 5 ]
echo @array[0..1]
echo @array[0...1]
echo @array[..3]
echo @array[3..]
echo @array[..]

方法

有两种类型的方法--基于字符串的和方法和基于数组的。方法的类型由调用方法时使用的符号表示。目前,只有两种支持的方法:$join()@split

let results = [ 1 2 3 4 5 ]
echo $join(results) @join # Both of these effectively do the same thing
echo $join(results, ', ') # You may provide a custom pattern instead

let line = "one  two  three  four  five"
echo @split(line) # Splits a line by whitespace

let row = "one,two,three,four,five"
echo @split(row, ',') # Splits by commas

字符串方法上的子串切片

echo $join(array)[3..6]

数组方法上的数组切片

let cpu_model = $(grep "model name" /proc/cpuinfo | head -1)
echo @split(cpu_model)[3..5]

命令

命令可以逐行编写,也可以在同一行上整体编写,使用分号分隔。

command arg1 arg2 arg3
command arg1 arg2 arg3
command arg1 arg2 arg3; command arg1 arg2 arg3; command arg1 arg2 arg3

管道和重定向标准输出

管道(|)和重定向(>)运算符用于操作标准输出。

command arg1 | other_command | another_command arg2
command arg1 > file

管道和重定向标准错误

^|^>运算符用于操作标准错误。

command arg1 ^| other_command
command arg1 ^> file

管道和重定向标准输出和标准错误

&|&>运算符用于操作标准输出和错误。

command arg1 &| other_command # Not supported yet
command arg1 &> file

条件运算符

Ion shell支持与Bash shell相同的&&||运算符。如果上一个命令以成功的退出状态退出,则&&运算符将执行下一个命令。如果上一个命令以失败退出,则||运算符将执行相反的操作。

test -e .git && echo Git directory exists || echo Git directory does not exist

if条件

还可以使用ifelse ifelse关键字执行更复杂的条件表达式。

let a = 5;
if test $a -lt 5
    echo "a < 5"
else if test $a -eq 5
    echo "a == 5"
else
    echo "a > 5"
end

While循环

while循环将评估提供的表达式以进行每次迭代,如果评估为成功的退出状态,则执行所有包含的语句。

let a = 1
while test $a -lt 100
    echo $a
    let a += 1
end

For循环

另一方面,for 循环将接受一个变量,后面跟一串值或范围表达式,并遍历所有包含的语句,直到所有值都被耗尽。如果变量是 _,它将被忽略。请注意,for 循环的引号规则是相反的,并且基于字符串的命令替换的值是通过行来分割的。

# Obtaining Values From a Subshell
for a in $(seq 1 10)
    echo $a
end

# Values Provided Directly
for a in 1 2 3 4 5
    echo $a
end

# Exclusive Range
for a in 1..11
    echo $a
end

# Inclusive Range
for a in 1...10
    echo $a
end

# Ignore Value
for _ in 1..10
   do_something
end

# Brace Ranges
for a in {1..10}
    echo $a
end

# Globbing
for a in *
    echo $a
end

命令替换

命令替换允许用户在子shell中执行命令,并将写入标准输出的数据用作扩展的替换。有两种执行命令替换的方法:基于字符串的命令替换和基于数组的命令替换。基于字符串的命令替换是标准的,它们通过将外部命令包裹在 $() 中来创建。基于数组的命令替换通过将命令包裹在 @() 中来表示。第一个只是捕获结果作为一个单独的字符串,精确地像它被写的那样,而第二个将接收到的数据分割成由空白字符分隔的单词。

尝试比较以下内容

for i in $(echo 1 2 3)
    echo $i
end
for i in @(echo 1 2 3)
    echo $i
end

基于字符串的命令替换切片

您可以对返回的字符串进行切片以获取其子字符串

echo $(echo one two three)[..3]

基于数组的命令替换切片

您可以对返回的数组进行切片以获取一组特定的元素

echo @(grep "model name" /proc/cpuinfo | head -1)[3..5]

函数

Ion shell 中的函数通过名称和一组变量来定义。函数将检查是否提供了正确数量的参数,如果提供了所有参数,则执行。

fn fib n
    if test $n -le 1
        echo $n
    else
        let output = 1
        let previous = 1
        for _ in 2..$n
            let temp = $output
            let output += $previous
            let previous = $temp
        end
        echo $output
    end
end

for i in 1..20
    fib $i
end

使用数组参数执行脚本

提供给脚本的参数存储在 @args 数组中。

执行的命令

script.ion one two three

脚本内容

for argument in @args
    echo $argument
end

输出

script.ion
one
two
three

依赖项

~1.6–3.5MB
~52K SLoC