编写 Powershell 脚本时,有两个选项可用:
- 调用 cmdlet
- 使用 .NET Framework 类库
说实话,我认为这对 PowerShell 的内容过于简单化了is.
首先值得指出的是 PowerShell(APIand the powershell.exe
主机应用程序)首先是在 .NET 中实现的,因此根据定义,PowerShell 中的所有内容都是“使用 .NET”。
例如,cmdlet 实际上是一个 .NET 对象 - 看看由返回的 CmdletInfo 对象Get-Command
:
PS C:\> (Get-Command Get-Command) |Format-List Name,CommandType,DLL,ImplementingType
Name : Get-Command
CommandType : Cmdlet
DLL : C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad36
4e35\System.Management.Automation.dll
ImplementingType : Microsoft.PowerShell.Commands.GetCommandCommand
看一下实现类型Microsoft.PowerShell.Commands.GetCommandCommand
- 它只是一个常规的 .NET 类 - 当您发出命令时Get-Command
,powershell 创建此类的实例,调用一些定义良好的方法,然后依次调用执行实际工作的 .NET 方法。
PowerShell 背后的整个哲学(或Monad,正如它最初的名字一样),开发时间最好花在专注于做好一件事的小型独立单元/功能上(amonad在最初的概念中),很像 UNIX 实用程序背后的原始哲学。
这个想法是,一旦你有了一个将这些单元耦合在一起的框架,你几乎可以compose通过连接更简单的单元来进行任何程序。
这个想法的具体实现就是cmdlet- 它有一些明确定义的输入绑定行为,允许您编写管道:
Get-AThing | Filter-TheThing | Write-ToAFile C:\path\to\file\name.ext
以我的愚见,像上面这样的管道是way比以下示例更具可读性:
[System.IO.File]::WriteAllText('C:\path\to\file\name.ext', [System.Linq.Enumerable]::Select([Thing]::EnumerateThings(),{param([thing]$in) $in -eq $someCriteria}))
这是主观的,但我想要表达的是,如果您顺便放弃了 cmdlet,那么您就在欺骗自己,无法利用 PowerShell 免费提供的一些开箱即用的核心功能
现在,对于实际问题:是的,直接调用单个 .NET 方法比调用 cmdlet 更快且开销更少,这反过来又使 PowerShell 运行一些额外的代码以最终仅包装相同的 .NET 方法调用。
话虽这么说,如果您运行的是较新版本的 PowerShell(4.0、5.0、5.1 或 6.0),则在许多情况下开销可以忽略不计。
例如,从磁盘读取文件比解析内存中已有的 .NET 方法调用链慢几个数量级(这就是 PowerShell 对您透明执行的操作),这仅仅是因为将电子从旋转的磁盘移动到磁盘控制器和内存总线进入内存是一个受光速限制的操作。
我个人的性能优化策略是在开始考虑 cmdlet 与直接方法调用之前,先检查我使用的算法/例程和数据结构。
如果我做了一些愚蠢的事情,使我的脚本需要 10 倍的 CPU 周期来计算,那么尝试追逐边际开销将无济于事。
如果您已达到该策略的极限,请考虑使用 C# 或 VB.NET 编写 cmdlet - 编译代码(几乎)总是比解释代码更快:-)