Crush:同时也是一种强大的现代编程语言的命令行shell

2020-08-07 17:58:49

Crush是一种尝试,它试图使传统的命令行shell也是一种现代编程语言。它具有人们期望从现代编程语言中获得的特性,如类型系统、闭包和词法作用域,但其语法同时适用于批处理和交互式shell使用。

如何调用命令、传递参数和设置管道没有改变,这是当前工作目录的核心概念。这意味着琐碎的调用,如ls或find..。|伯爵看起来一样,但在兜帽下他们很不一样,除此之外几乎所有的东西都是不同的。

让我们从两个简单的命令开始;列出当前目录中的文件,并检查当前目录中有多少个文件:

Crush>;lsuser size Modified type fileliljencrantz 2279 2020-03-07 13:00:33+0100文件理想liljencrantz 4096 2019-11-22 21:56:30+0100目录目标...crush>;ls|count14。

这些看起来都很眼熟。但外表是有欺骗性的。被调用的ls命令是一个Crush内置命令,输出不是通过Unix管道发送的,而是通过Rush通道发送的。该命令不会将其理解为一系列字节,而是一种行表,而Crush为您提供了类似SQL的命令来对数据行进行排序、筛选、聚合和分组。

Crush>;ls|sor^sizeuser size Modified type fileliljencrantz 31 2019-10-03 13:43:12+0200文件.gitignoreliljencrantz 75 2020-03-07 17:09:15+0100文件生成。rsliljencrantz 491 2020-03-07 23:50:08+0100文件货物.tomlliljencrantz 711 2019-10-03 14:19:46+0200。}User Size Modified type fileliljencrantz 4096 2019-11-22 21:56:30+0100目录targetliljencrantz 4096 2020-02-22 11:50:12+0100目录testsliljencrantz 4096 2020-03-16 14:11:39+0100目录.idealiljencrantz 4096 2020-02-15 00:12:18+0100目录示例_dataliljencrantz 4096 2020-03-14。

因为Crush Output是一个带有列的行流,所以像按任意列排序或基于任意逻辑表达式对这些列进行过滤这样的操作很容易,而且因为用来做这件事的组件是通用的和可重用的,你可以简单地对来自任何源的数据做同样的事情,比如JSON文件,http请求等。

在传统的shell中,I/O是以二进制流的形式完成的。因为Crush流是类型化的,所以I/O发生的方式不同。Crush具有用于序列化和反序列化各种文件格式的命令对。例如,使用json:from和json:to分别反序列化和序列化json数据。这些经理都像你一样工作,我期望:

#将ls命令的输出转储到json格式的listing.json文件(json格式)crush>;ls|json:to./listing.json#将cargo.toml文件读取为toml文件,提取依赖项-fieldcrush>;(toml:from cargo.toml):依赖项#获取网页并将其写入文件(http";https://isitchristmas.com/";):Body|bin:to./isitchristmas.。

如果您没有向任何反序列化程序命令提供输入文件,该命令将从输入读取,该输入必须是二进制流或二进制流,例如(http";https://jsonplaceholder.typicode.com/posts/1";):body|json:from。

如果您不向其中一个序列化程序命令提供输出文件,则该命令会将输出序列化为二进制流,作为管道输出:

Crush序列化程序之一Pup是Crush的原生文件格式。Pup-format是基于协议的,它的模式可以在这里找到。Pup的优点是所有的crush类型,包括类和闭包,都可以无损地序列化成这种格式,但是因为Pup是特定于crush的,所以对于与其他语言共享数据是没有用的。

Crush允许您直接在shell中对整数和浮点数执行数学计算,主要使用几乎任何其他编程语言中使用的相同数学运算符。

唯一的例外是/运算符用于构建文件和路径(稍后将详细介绍),因此除法是使用//运算符完成的。

与大多数语言一样,值之间的比较使用>;、<;、<;=、>;=、==和!=进行。不同类型的值之间的所有比较都是假的。

Crush也有与模式和匹配相关的运算符。=~和!~用于检查模式是否与输入匹配:

#%字符是globscush>;%.txt=~foo.txttrue#中的通配符#这是构造和匹配正则表达式crush>;re";ab+c";=~";abbbbc";true的方式。

正则表达式还支持使用~(REPLACE ONCE)和~~(Replaceall)运算符进行替换,这两个运算符是三进制运算符:

如前所述,许多Crush命令对表格数据流进行操作。该表流中的单个单元格可以是各种类型中的任何一种,包括字符串、整数、浮点数、列表、二进制数据或另一个表流。

Crush>;ps|head 5pid PPID状态用户CPU名称1 0休眠根4.73/sbin/init 2 0休眠根0[kthreadd]3 2空闲根0[RCU_GP]4 2空闲根0[RCU_PAR_GP]6 2空闲根0[kworker/0:0h-kblock]。

当然,有些命令输出单个值,例如pwd,它将当前工作目录输出为文件类型的单个元素。

CRUSH&gT;SOME_NUMBER:=4#the:=运算符声明一个新变量>;SOME_NUMBER*520。

与任何正常的编程语言一样,变量可以是类型系统支持的任何类型。没有隐式类型转换。请注意,一些数学运算符是在类型之间定义的,因此,例如,将整数与浮点数相乘得到浮点数。

Crush支持命名参数和未命名参数。通常可以使用一种、另一种或两者的组合。以下三个调用是等效的。

将布尔参数传递给命令是很常见的,这就是为什么Crush为它提供了一种特殊的简写语法。传入--foo等效于传入foo=true。

有时,您希望将一个命令的输出用作另一个命令的参数,就像bash中的子shell一样。这与使用输出作为输入不同,它是使用()完成的:

在Crush中,大括号({})用于创建闭合。将闭包分配给avariable是创建函数的方式。

调用闭包时传递并添加到调用的本地作用域的任何命名参数:

为了增加类型安全,您可以在闭包开始时声明闭包需要哪些参数。

下面的闭包要求调用方提供参数a,并允许调用方指定参数b,该参数必须为整数类型。如果调用方没有指定它,它将回退到默认值7。

此外,@运算符可用于创建所有未命名参数的列表,@@运算符可用于创建参数列表中未提及的所有命名参数的列表。

在命令调用期间还使用@和@@运算符来执行镜像操作。下面的代码创建一个lss函数,该函数调用ls命令并将所有参数传递给它,并通过SELECT命令通过管道传输输出,以仅显示输出中的一列。

任意类型类型对的字典(有些类型不能用作键!)。

在使用Crush时,help和dir命令非常有用。前者显示帮助消息,后者列出一个值的内容。

Crush;help sorte列:基于列的字段排序输入示例:ps|sor^cpucush>;dir list[TYPE,TRUNCATE,REMOVE,CLONE,OF,__CALL_TYPE__,__SETITEM__,POP,PUSH,EMPTY,LEN,PEEK,NEW,CLEAR]。

当前工作目录中的所有文件都是本地命名空间的一部分。是指向当前工作目录的文件对象。在Crush中使用/运算符将两个文件目录元素连接在一起。

这意味着在很大程度上,在Crush中使用文件是非常简单和方便的。

压缩光盘(&>;CD)..。#这做了您认为的事情&>cd/#就像这件事一样。

运算符的右侧是标签,而不是值,因此./foo引用当前工作目录中名为foo的文件,并且与名为foo的任何变量的内容无关。

可以使用:运算符访问成员。大多数其他语言倾向于使用.,但这是文件名中非常常见的字符,所以Crush需要找到其他字符。

大多数类型都有几种有用的方法。文件有EXISTS和STAT,这符合您的预期。

Crush>;.:Existstrucechash.:STAT{IS_DIRECTORY:TRUE,IS_FILE:FALSE,IS_SYMBLINK:FALSE,信息节点:50856186,NLINK:8,MODE:16877,LEN:4096}CRUSH>;(.:STAT):IS_FILEFALSE。

实际上将存储在all_the_files变量中的只是一个流。在执行find命令的三个线程开始阻塞之前,将急切地评估少量的输出行。如果流被使用,例如通过写入。

然后所有的地狱都会在你的屏幕上爆发,因为成千上万的线条被打印到你的屏幕上。

这将消耗来自流的一行输出。此命令可以重新执行,直到流为空。

Crush提供了许多命令,可以使用类似SQL的语法对任意数据流进行操作。这些命令使用^foo等字段说明符指定它们操作的数据流中的列:

与SQL不同的是,这些命令都在输入流上操作,这意味着它们可以按任何顺序组合,输入源可以是各种格式的文件/http资源,也可以是ps、find等命令的输出。

*运算符用于乘法,因此Crush使用%作为通配符。?仍用于单字符通配符。

压缩&>ls%.txt用户大小修改类型fileliljencrantz 21303 2020-03-30 13:40:37+0200file/home/liljencrantz/src/crush/README.mdcrush>;ls?用户大小修改类型fileliljencrantz 752020-03-07 17:09:15+0100file/home/liljencrantz/src/crush/build.rs。

运算符%%用于递归执行子目录中的全局操作。另一种查看相同语法的方式是说%and?匹配除/之外的任何字符,而%%也匹配/。

#统计粉碎源码行数src/%%.rs|count。

通配符不会自动展开,它们作为globobject传递给命令,命令选择与glob匹配的对象。如果您希望在本身不执行此操作的命令中执行GLOB扩展,请使用GLOB对象的:filesmethod执行此操作:

Crush>;l:=(列表:共1 2 3)crush>;l[1,2,3]crush>;l:peek3crush>;l:pop3crush>;l:len2crush>;l[1]2crush>;l[1]=7crush>;l[1,7]crush>;Help ltype列表整数可变项目列表,通常为同一类型*__CALL_TYPE_返回指定元素类型的列表类型*__getitem__返回指定基目录中的文件或子目录*__setitem__为指定索引处的元素赋值*清除从列表中移除所有元素*clone创建列表的副本*空True如果列表中没有元素*len列表中的元素数*new创建具有指定元素类型的新列表*创建包含所提供元素的新列表*。列表中的元素*弹出删除列表中的最后一个元素*推送将元素推送到列表末尾*删除指定索引处的元素*截断删除超过指定索引的所有元素。

Crush>;d:=(dict字符串整数):newcrush>;d[";foo&34;]=42crush>;d[";foo";]42crush>;Help dtype dict字符串整数从一组值到另一组值的可变映射*__CALL_TYPE_返回具有指定键和值类型的DICT类型*__getitem__返回指定键映射到的值*__setitem__创建新映射或替换现有映射*清除删除此DICT中的所有映射*CLONE创建映射ST与此相同的新DICT*如果DICT*LEN中没有映射,则为空True。

压缩&>开始:=时间:立即压缩&>某件需要大量时间的东西&>结束:=时间:立即压缩&>回显(";我们在物品上花费了{})";:格式结束-开始)4:06。

有意义的数学运算符是为时间和持续时间定义的。从一个时间减去另一个时间会得到一个持续时间。将两个持续时间相加会产生一个持续时间。将持续时间乘以整数或除以整数得到持续时间。

许多命令的输出是表流,即由具有相同结构的行组成的流数据结构。有些命令,如cat,则输出二进制流。

这些流不能倒带,只能消费一次。这有时是至关重要的,因为这意味着人们可以处理比计算机内存更大的数据集,甚至可以处理无限的数据集。

但有时,流式数据集是不方便的,特别是当一个人想要两次使用同一个数据集的时候。

压缩文件:=lscush>;文件用户大小修改类型文件liljencrantz 13072020-03-26 01:08:45+0100文件理想liljencrantz 40962019-11-22 21:56:30+0100目录目标liljencrantz 40962020-03-27 09:18:25+0100目录测试sliljencrantz 13072020-03-24 17:20:00+0100file cargo.lockiljencrantz 40962020-02-。25+0100目录srcliljencrantz 479 2020-03-24 17:20:00+0100文件货架.tomlliljencrantz 4096 2020-03-29 01:29+0100目录.gitliljencrantz 8382 2020-03-29 00:54:13+0100文件至todoliljencrantz 75 2020-03-07 17:09:15+0100文件构建.rsliljencrantz 711 201。档案。

请注意,第二次显示文件时没有输出,因为已经使用了etable_stream。

输入MATERIALIZE命令,该命令接受任何值,并递归地将所有瞬态组件转换为等价但完全在内存中的形式。

CRUSH&gT;MATERIALIZED_FILES:=(ls|MATERIALIZE)CRUSH&gT;Materialized_filesuser size Modified type fileliljencrantz 1307 2020-03-26 01:08:45+0100文件理想liljencrantz 40962019-11-22 21:56:30+0100目录target liljencrantz 40962020-03-27 09:18:25+0100目录testsliljencrantz 13072020-03-24 17:20:00+0100文件货物.lockliljencrantz 40962020。35:25+0100目录srcliljencrantz 479 2020-03-24 17:20:00+0100文件货物.tomlliljencrantz 4096 2020-03-29 01:29:52+0100目录.gitliljencrantz 8382 2020-03-29 00:54:13+0100文件到doiljencrantz 75 2020-03-07 17:09:15+0100文件生成。rsliljencrantz 7。Materialized_filesuser size Modified type fileliljencrantz 1307 2020-03-26 01:08:45+0100文件理想liljencrantz 40962019-11-22 21:56:30+0100目录target liljencrantz 40962020-03-27 09:18:25+0100目录testsliljencrantz 13072020-03-24 17:20:00+0100文件货物.lockliljencrantz 40962020。35:25+0100目录srcliljencrantz 479 2020-03-24 17:20:00+0100文件货物.tomlliljencrantz 4096 2020-03-29 01:29:52+0100目录.gitliljencrantz 8382 2020-03-29 00:54:13+0100文件到doiljencrantz 75 2020-03-07 17:09:15+0100文件生成。rsliljencrantz 7。

当然,Crush有一个IF命令,以及For、WHILE和LOOP循环,可以使用Break和Continue来控制。

CRUSH&>HELP IFIF条件:布尔IF-子句:COMMAND[ELSE-子句:COMMAND]有条件地执行一个命令一次。如果条件为真,则执行if子句。否则,将执行Else-子句(如果指定)。示例:如果(./SOME_FILE:STAT):IS_FILE{ECHO";它是一个文件!";}{ECHO";它不是一个文件!";}对于[name=]iterable:(table_stream|table|dict|list)Body:命令对Iterable中的每个元素执行一次Body。示例:for(Seq){echo(";Lap{}";:格式值)}。

显然,人们有时需要呼唤外部命令。目前,在Crush中这样做的功能有点原始。如果给定名称的内部命令不存在,Crush将查找外部命令,如果找到外部命令,则使用该命令。但Crush不移交tty或模拟Atty,因此交互式终端程序无法工作,用转义序列美化其输出的命令可能会失败。

Crush的这一部分应该被视为概念证明,但大多数非交互命令仍按预期工作:

首先,像git status这样的子命令被映射到像git:status这样的方法调用。这样您就不必引起子命令名,例如git";status";。

其次,命名参数被透明地转换为选项。单字符参数名称被转换为带有单个连字符的选项,多字符参数名称被转换为带有两个连字符的GNU样式的长选项,例如,git:Commit m=";hello";被转换为git Commit-m&m";hello";和git:Commit message=";hello";被转换为git Commit--message";hello";。

第三,布尔值为true的命名参数被简单地转换为不带值的options,因此例如git:Commit--a--append(或git:Commit a=true append=true)被转换为git Commit-a--append。

当涉及到作业控制、终端仿真和其他集成点时,还需要进一步的工作。

闭包将被序列化、使用ssh传输到远程主机、反序列化并在远程主机上执行(粉碎外壳必须位于远程主机上的默认路径中)。一旦命令已经执行,并且闭包的输出在本地机器上被序列化、传输、反序列化并用作Remote:EXEC命令的输出。

Point:=(类)Point:__init__={|x:Float y:Float|this:x=x this:y=y}Point:LEN={||数学:sqrt this:x*this:x+this:y*this:y}Point:__add__={|Other|Point:new x=(this:x+ther:x)y=(this:y+y+ther:y)}p:=(Point:new x=1.0 y=2.0)p:LEN。

Crush支持单一继承(通过将父级传递给classcommand)。Class命令将创建一个新结构,其中包含名为new的方法。调用new时,将创建该类的新实例。如果定义了__init__方法,new将调用它,并将所有参数传递给它。

通过将方法添加到类来添加方法,通过将成员变量添加到__init__中的实例(This)来添加成员变量。

Crush与PowerShell共享其大部分设计目标。我认为PowerShell是微软有史以来最酷、最有趣的创新之一。也就是说,我发现在实践中使用PowerShell经常感觉笨重和恼人,特别是在交互使用时。我还觉得将ashell与COM对象捆绑在一起不太合适。

我想做一些类似的事情,但是语法更精简,并且我觉得类型系统更合适。

从表面上看,Crush看起来与Nushell相同,但没有那么精致。Crush lash语法突出显示、制表符完成,并且屏幕呈现效果较差。但这是因为Crush现在的重点是创建一种定义良好、功能强大且方便的语言,支持算术运算符之类的东西。

.