当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 如何使用 PowerShell AST 通过脚本获取元数据

作者:精品下载站 日期:2024-12-14 20:48:22 浏览:10 分类:玩电脑

如何使用 PowerShell AST 通过脚本获取元数据


PowerShell AST 本质上将代码分解为分层树,每个元素代表树的一部分,使脚本具有自我意识。

相关:想要更多 PowerShell 优点吗?我们有数十个关于 PowerShell 所有内容的教程!

你看过电影《盗梦空间》吗?这是关于一个可以进入某人的梦并从他们的潜意识中窃取东西的人的故事。这是一次让您质疑周围现实的旅行。它给我的感觉就像《黑客帝国》一样,现实并不像你想象的那样。这是一些有深度的东西。这与 PowerShell 无关,但它让您了解 PowerShell 抽象语法树 (AST) 的作用。

想象一个具有自我意识的 PowerShell 脚本。想象一个 PowerShell 脚本可以读取自身,甚至可以根据其自身包含的内容生成其他脚本。将其视为元脚本。这是一个巧妙的概念,并且有很多实际用途!这就是 PowerShell AST 可以做的事情。 PowerShell AST 本质上将代码分解为分层树,每个元素代表树的一部分。

在本文中,我将介绍如何使用 PowerShell AST 并通过几个示例来说明它如何解析 PowerShell 代码。

了解 Language.Parser 类

首先,您需要熟悉 System.Management.Automation.Language.Parser 类。这是一个包含一些适用的静态方法的类,我们可以使用它们来读取脚本和代码。该类有两个您经常使用的方法,分别是 ParseInput()ParseFile(),它们本质上做同样的事情。 ParseInput() 将代码读取为大字符串,而 ParseFile() 帮助您转换包含 PowerShell 代码的文本文件,并将其转换为字符串进行解析。两者最终都会得到相同的结果。

假设我有一个包含以下几行的简单脚本:

Write-Host 'I am doing something here'
Write-Verbose 'I am doing something here too'
Write-Host 'Again, doing something.'
$var1 = 'abc'
$var2 = '123'

从脚本本身中,我想确定对我拥有的每个 cmdlet 和每个变量的所有引用。为此,我首先需要找到一种方法将整个脚本内容作为一个大字符串获取。我可以通过使用 PowerShell AST 对象的 $MyInitation.MyCommand.ScriptContents 属性来做到这一点。我只需将其添加为脚本中的最后一行并执行它。

一旦我有了脚本内容,我就可以将其传递给上面提到的 ParseInput() 方法,以从我的脚本构建一棵“树”。我将用以下内容替换 $MyInitation.MyCommand.ScriptContents 引用:

[System.Management.Automation.Language.Parser]::ParseInput($MyInvocation.MyCommand.ScriptContents, [ref]$null, [ref]$null)

这让我得到如下所示的输出:

PS> C:\test.ps1
 I am doing something here
 Again, doing something.
 
 
 Attributes         : {}
 UsingStatements    : {}
 ParamBlock         :
 BeginBlock         :
 ProcessBlock       :
 EndBlock           : Write-Host 'I am doing something here'
                      Write-Verbose 'I am doing something here too'
                      Write-Host 'Again, doing something.'
                      $var1 = 'abc'
                      $var2 = '123'
                      [System.Management.Automation.Language.Parser]::ParseInput($MyInvocation.MyCommand.ScriptContents,
                       [ref]$null, [ref]$null)
 DynamicParamBlock  :
 ScriptRequirements :
 Extent             : Write-Host 'I am doing something here'
                      Write-Verbose 'I am doing something here too'
                      Write-Host 'Again, doing something.'
                      $var1 = 'abc'
                      $var2 = '123'
                      [System.Management.Automation.Language.Parser]::ParseInput($MyInvocation.MyCommand.ScriptContents,
                       [ref]$null, [ref]$null)
 
 Parent             

但这并没有多大好处。我想要一种方法来研究这个问题并只找到脚本中包含的函数和变量。为此,我需要将 AST 分配给一个变量。我将调用我的 $ast

PS> $ast=C:\test.ps1

这为我提供了一个具有我现在可以使用的各种方法和属性的对象。

PS> $ast | gm
 
 
    TypeName: System.Management.Automation.Language.ScriptBlockAst
 
 Name               MemberType Definition
 ----               ---------- ----------
 Copy               Method     System.Management.Automation.Language.Ast Copy()
 Equals             Method     bool Equals(System.Object obj)
 Find               Method     System.Management.Automation.Language.Ast Find(System.Func[System.Management.Automatio...
 FindAll            Method     System.Collections.Generic.IEnumerable[System.Management.Automation.Language.Ast] Find...
 -- SNIP --

使用 FindAll() 方法查找项目

最有用的方法是 FindAll() 方法。这是一种允许您查询 AST 本身以查找特定类型的语言结构的方法。在我们的例子中,我们正在寻找函数调用和变量赋值。

为了只找到我们正在寻找的语言结构,我们必须首先弄清楚每种类型代表什么类。在我们的示例中,这些类是用于函数调用的 CommandAst 和用于变量赋值的 VariableExpression。您可以在 MSDN System.Management.Automation.Language 命名空间页面查看所有不同的类类型。

在这里我将找到所有函数参考。

PS> $ast.FindAll({$args[0] -is [System.Management.Automation.Language.CommandAst]}, $true)
 
 CommandElements    : {Write-Host, 'I am doing something here'}
 InvocationOperator : Unknown
 DefiningKeyword    :
 Redirections       : {}
 Extent             : Write-Host 'I am doing something here'
 Parent             : Write-Host 'I am doing something here'
 
 CommandElements    : {Write-Verbose, 'I am doing something here too'}
 InvocationOperator : Unknown
 DefiningKeyword    :
 Redirections       : {}
 Extent             : Write-Verbose 'I am doing something here too'
 Parent             : Write-Verbose 'I am doing something here too'
 
 CommandElements    : {Write-Host, 'Again, doing something.'}
 InvocationOperator : Unknown
 DefiningKeyword    :
 Redirections       : {}
 Extent             : Write-Host 'Again, doing something.'
 Parent             : Write-Host 'Again, doing something.'

现在让我们也找到所有变量赋值。

PS> $ast.FindAll({$args[0] -is [System.Management.Automation.Language.VariableExpressionAst]},$true)
 
 ariablePath : var1
 Splatted.   : False
 StaticType  : System.Object
 Extent.     : $var1
 Parent.     : $var1 = 'abc'
 
 VariablePath : var2
 Splatted.    : False
 StaticType.  : System.Object
 Extent.      : $var2
 Parent.      : $var2 = '123'
 
 VariablePath : MyInvocation
 Splatted.    : False
 StaticType.  : System.Object
 Extent.      : $MyInvocation
 Parent.      : $MyInvocation.MyCommand
 
 VariablePath : null
 Splatted.    : False
 StaticType.  : System.Object
 Extent.      : $null
 Parent.      : [ref]$null
 
 VariablePath : null
 Splatted.    : False
 StaticType.  : System.Object
 Extent.      : $null
 Parent.      : [ref]$null

您可以看到每个构造现在都成为您可以使用的对象。您现在已经掌握了以任何您想要的方式分解脚本的知识。通过使用 AST,您的 PowerShell 脚本现在可以变得具有自我意识。当你的脚本开始试图自己取代你的工作时,请不要责怪我!

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯