主题 : 使用F#编写PowerShell模块
千山同一月 万户尽皆春 千江有水千江月 万里无云万里天
级别: 总版主

UID: 998
精华: 0
发帖: 605019
威望: 528932 点
无痕币: 59 WHB
贡献值: 0 点
在线时间: 62042(时)
注册时间: 2008-12-25
最后登录: 2024-04-19

0 使用F#编写PowerShell模块


▲F#和PowerShell模块
作为可能是人类世界最强大的Shell,PowerShell最大的特点是能够直接在命令间传递.NET对象,而支持这种能力的命令被称作cmdlet。自己编写PowerShell模块,就能给PowerShell扩展更多的cmdlet。
为何要写本文
通常来讲,PowerShell模块有两种:脚本模块和二进制模块。顾名思义,脚本模块基本上就是一组PowerShell脚本,而二进制模块,实质上是一个.NET类库。微软对于二进制模块的官方文档中,只提供了C#的范例;但鉴于F#和C#几乎是同等强大的,我在想,使用F#也能编写出可以工作的PowerShell模块。经过一小段时间的研究和整理,我决定将可行的方案分享给之家的朋友们。
任务目标
使用F#编写一个PowerShell模块,提供一个名为Get-Sum的cmdlet,完成对一个整数数组求和的功能。至于为什么选择“求和”,是因为递归求和函数通常是F#这类函数式编程语言第一课必学的例子,这种递归的思想也算是函数式语言的特色之一。
正式开始
1、建立项目
开始上手操作之前,确保你安装了.NET Core SDK,并经过适当配置,可以在任意位置使用“dotnet”命令。
打开PowerShell,在你通常保存项目代码的地方创建一个目录,作为整个项目的根。在这个目录下,使用dotnet new命令创建一个F#的类库项目和一个解决方案文件,并将项目添加进解决方案中。

如此,我们便建立了解决方案fsharp-module和F#类库工程testmodule。
2、添加用于编写/生成PowerShell模块的组装件
使用dotnet add package为工程testmodule添加名为PowerShellStandard.Library的组装件,这个组装件由微软提供,并将为工程提供System.Management.Automation名称空间和必要的类型。

如此,我们建立的F#类库已经可以用于编写PowerShell模块了。
3、编写代码
使用自己喜爱的代码编辑器修改testmodule\Library.fs,代码内容如下,我将在代码的下方解释一些要点。

注0:代码中的向右的箭头是->两个字符,向右的三角形是|>两个字符。这种连体字由著名字体FiraCode提供。灰色注释是VSCode相关插件自动生成的;
注1:此名称空间包含了编写PowerShell模块必须的类型;
注2:任何作cmdlet存在的类型必须加上Cmdlet属性(Attribute)。任何cmdlet的名称都由两部分组成:动词(Verb)和名词(Noun),最终形成“Verb-Noun”形式的名称。在这里,我们定义VerbName为Get,NounName为Sum,即得到最终的cmdlet名称Get-Sum。需要注意的是,Verb的选择需要遵守微软的规范。如果你执意要使用奇怪的Verb,构建代码时和导入模块时就会产生警告。任何cmdlet类都必须继承于PSCmdlet类;
注3:一个很经典的递归求和函数。我知道这功能可以干脆使用Array.sum方法来实现,但作为函数式语言的标志性操作之一,展示出来可以展现函数式语言和命令式语言的某种区别;
注4:Parameter属性(Attribute)标记的InputArray属性(Property)用来接收传递过来的参数对象。可以在Parameter属性(Attribute)中指定关于这个参数的更多细节,比如参数的位置、能否从管道中接收等等;
注5:重写基类(部分Java程序员会称作父类,其实都一样)的EndProcessing方法(命令处理完成后)执行求和操作并输出结果。类似的可供重写的虚函数还有BeginProcessing、ProcessRecord和StopProcessing,它们分别表示命令开始执行之前、命令执行的主体和命令中断时的过程。原则上求和应放在ProcessRecord中执行,但由于我们命令的功能很简单,干脆直接在EndProcessing里直接输出求和的结果。
4、编译模块并测试
使用dotnet publish命令发布这个类库。之所以使用publish而不是build,是因为F#程序还依赖另一个组装件:FSharp.Core来运行,而build默认不包含该组装件,该模块将不能被导入PowerShell中。
发布完成后,使用Import-Module命令导入我们编写的模块,并测试功能。

其实,别看这个cmdlet输出的是看似文本的5050,其实它输出的是一个System.Int32,你可以使用GetType()函数来证明这一点:

也正是这种在管道中传递对象的能力,使PowerShell比别的一些Shell拥有更高的潜力。
级别: 十方秋水

UID: 88
精华: 0
发帖: 130030
威望: 221631 点
无痕币: 107022 WHB
贡献值: 0 点
在线时间: 51875(时)
注册时间: 2008-03-18
最后登录: 2024-04-18

进来看看,了解一下。
千山同一月 万户尽皆春 千江有水千江月 万里无云万里天
级别: 总版主

UID: 998
精华: 0
发帖: 605019
威望: 528932 点
无痕币: 59 WHB
贡献值: 0 点
在线时间: 62042(时)
注册时间: 2008-12-25
最后登录: 2024-04-19

这个可以看看,了解一下。
知足常乐
级别: 十方秋水

UID: 23
精华: 1
发帖: 261101
威望: 117114 点
无痕币: 2298 WHB
贡献值: 0 点
在线时间: 9389(时)
注册时间: 2007-11-24
最后登录: 2024-04-19

不懂这个,看看了
事能知足心常乐 人到无求品自高
影视交流
级别: 论坛版主

UID: 33334
精华: 0
发帖: 62427
威望: 501126 点
无痕币: 18456 WHB
贡献值: 1140852 点
在线时间: 60609(时)
注册时间: 2009-03-18
最后登录: 2024-04-19

对这个可以加深了解一下子。
标签
十方秋水,漫长旅途.
级别: 十方秋水

UID: 116295
精华: 0
发帖: 111450
威望: 122808 点
无痕币: 4249 WHB
贡献值: 0 点
在线时间: 6088(时)
注册时间: 2011-03-27
最后登录: 2024-03-09

感谢楼主分享
Total 0.054505(s) query 4, Time now is:04-19 03:50, Gzip enabled 粤ICP备07514325号-1
Powered by PHPWind v7.3.2 Certificate Code © 2003-13 秋无痕论坛