使用旧的 Rust 2015
1.0.5 |
|
---|---|
1.0.3 |
|
1.0.0 |
|
0.1.7 |
|
0.0.4 |
|
19 在 #ion 中排名
每月下载 22 次
390KB
8K SLoC
Ion Shell
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条件
还可以使用if
、else if
和else
关键字执行更复杂的条件表达式。
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