如何高效测试一个 .PS1 脚本是一组函数的“模块”还是普通脚本?

huangapple go评论56阅读模式
英文:

How to efficiently test whether a .PS1 script is a "module" of functions or a normal script?

问题

我的组织有大量的PowerShell脚本。其中一些是直接处理逻辑/执行某些操作的脚本,但其他一些本质上是“模块”(但不是实际模块),它们只包含我们导入并由其他脚本使用的常见函数。

我正在尝试找出一种方法来确定.PS1文件是一个“脚本”,还是一个“模块”,它仅包含函数,不处理任何逻辑。

我的想法是捕获脚本的内容,然后查看它是否仅包含一个或多个:function X { * },而没有其他内容。可能使用正则表达式来匹配这种模式。

但这看起来有点混乱,所以我想知道是否有人有更好的方法?

我知道可能会有人建议我们按照PowerShell的意图构建模块,但这些都是已经存在的脚本,已经开发好了,我肯定没有权力告诉整个组织更改它们编写脚本的方式。

英文:

My organization has a ton of PowerShell scripts. Some of those are straight scripts that process logic/perform certain actions, but others are essentially "modules" (but not actual modules), in that they only contain common function we use which are imported and used by other scripts.

I'm trying to figure out a way to determine if a .PS1 file is a "script", or if it's a "module" in that it only contains functions and doesn't process any logic.

My thoughts are I'd just capture the contents of the script and see if it contains only one or more of: function X { * }, and nothing else. Probably using regex to match that pattern.

But that seems messy, so I was wondering if anyone had any ideas how I can do it better?

I know I'll probably get some people recommending we build modules the way PowerShell intended, but these are all pre-existing scripts that have already been developed and I certainly don't have the authority to tell my entire organization to change the way they write scripts.

答案1

得分: 4

Sure, here are the translated parts:

My thoughts are I'd just capture the contents of the script and see if it contains only one or more of: function X { * }, and nothing else.

我的想法是,我只会捕获脚本的内容,看它是否只包含一个或多个这样的内容:function X { * },而没有其他内容。

That's a great plan!

这是一个很好的计划!

[...] Probably using regex to match that pattern.

也许可以使用正则表达式来匹配这个模式。

That's a terrible plan!

这是一个糟糕的计划!

Instead of regex, use PowerShell's own parser to parse the existing scripts:

不要使用正则表达式,而是使用PowerShell的自带解析器来解析现有的脚本:

Now that we've analyzed the top-level statements in all the scripts, we can start filtering them based on the expectation that most are probably "module-like":

现在,既然我们已经分析了所有脚本中的顶层语句,我们可以开始根据预期,大多数脚本可能都类似于"模块"来进行筛选:

$results |Where-Object { $_.FunctionCount -gt 0 -and $_.PipelineCount -eq 0 } |ForEach-Object {
    Write-Host "Script at '$($_.Path)' looks like a module! No pipelines, just functions!"
}
$results |Where-Object { $_.FunctionCount -gt 0 -and $_.PipelineCount -eq 0 } |ForEach-Object {
    Write-Host "位于'$($_.Path)'的脚本看起来像一个模块!没有管道,只有函数!"
}
英文:

> My thoughts are I'd just capture the contents of the script and see if it contains only one or more of: function X { * }, and nothing else.

That's a great plan!

> [...] Probably using regex to match that pattern.

That's a terrible plan!

Instead of regex, use PowerShell's own parser to parse the existing scripts:

using namespace System.Management.Automation.Language

# start by enumerating all the script files in scope
$results = Get-ChildItem folder\with\script\files -Filter *.ps1 -Recurse |ForEach-Object {
  # construct an object that will hold the output for each file analyzed
  $result = [pscustomobject]@{
    Name          = $_.Name
    Path          = $_.FullName
    FunctionCount = 0
    PipelineCount = 0
    OtherCount    = 0
    HadErrors     = $false
  }

  try {
    # attempt to parse the script file
    $scriptAst = [Parser]::ParseFile($_.FullName, [ref]$null, [ref]$null)

    # inspect the top-level statements in the resulting AST, capture the counts of particularly interesting syntax elements
    $result.FunctionCount = $scriptAst.EndBlock.Statements.Where({$_ -is [FunctionDefinitionAst]}).Count
    $result.PipelineCount = $scriptAst.EndBlock.Statements.Where({$_ -is [PipelineAst]}).Count
    $result.OtherCount    = $scriptAst.EndBlock.Statements.Where({$_ -isnot [PipelineAst] -and $_ -isnot [FunctionDefinitionAst]}).Count
  }
  catch {
    # in case of failure we want to report that to the caller
    $result.HadErrors = $true
  }

  # output the details captured
  $result
}

Now that we've analyzed the top-level statements in all the scripts, we can start filtering them based on the expectation that most are probably "module-like":

$results |Where-Object { $_.FunctionCount -gt 0 -and $_.PipelineCount -eq 0 } |ForEach-Object {
    Write-Host "Script at '$($_.Path)' looks like a module! No pipelines, just functions!"
}

huangapple
  • 本文由 发表于 2023年3月21日 00:18:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/75792789.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定