为什么此PowerShell脚本中有这么多StringConstantExpressionAst?

时间:2019-10-19 23:25:54

标签: powershell abstract-syntax-tree canonicalization

我正在对某些PowerShell数据集进行规范化,一个处理步骤是将所有变量替换为X,并将所有字符串文字替换为Y,以便我可以检测和删除几乎重复的副本。

但是,我注意到对于规范化后的许多脚本,整个脚本可以归结为很多Y和一些X,几乎没有其他任何代码。这不是我所期望的,因为脚本中只有少数变量和字符串文字。

要查找所有字符串文字,我使用了以下命令:

$Strings = $AST.FindAll({$args[0] -is System.Management.Automation.Language.StringConstantExpressionAst]}, $true)

为解决此问题,我使用ShowPSAst (PowerShell AST visualization tool)可视化了一个示例脚本,其中上述问题很明显。

原始脚本如下:

 Describe "Files" -Tag OSX,Linux {
    It "is utf-8 encoded" {
        $true | Should Be $false
    }
    It "uses Unix-style line endings" {
        $true | Should Be $false
    }
    It "has a shebang" {
        $true | Should Be $false
    }
}
Describe "Placeholder for Nano tests" -Tag Nano {
}

规范化后,我得到以下信息:

Y Y -Tag Y,Y {
    Y Y {
        X | Y Y X
    }
    Y Y {
        X | Y Y X
    }
    Y Y {
        X | Y Y X
    }
}
Y Y -Tag Y {
}

上述脚本的AST可视化摘录:

Part of the AST visualization of the above script

请注意,图像右面板中突出显示的部分与左面板中的AST节点CommandAST相对应,然后,AST节点StringConstantExpressionAst作为子节点。看看这些AST节点,就可以理解为什么我的规范版本中有那么多Y。但是,令我感到困惑的是,为什么突出显示的代码中几乎所有的单独标记都被视为StringContantExpressionAst。我希望只有"Placeholder for Nano tests"被视为字符串文字。

准确地说,我希望

Describe "Placeholder for Nano tests" -Tag Nano

要转化为

 Describe Y -Tag Nano

而不是

Y Y -Tag Y

我并不是真正地独自使用PowerShell,也不知道它的复杂性,因此如果我缺少基本的东西,我深表歉意,在此感谢我对理解PowerShell行为的任何帮助。

1 个答案:

答案 0 :(得分:1)

PowerShell是一种解释型语言,这意味着它在运行代码之前不会在代码的某些部分附加含义。在您的情况下,它不知道单词“ Describe”是指Pester模块中的Describe函数(它可能甚至没有导入到您的会话中),它同样意味着一个名为“ Describe”的外部程序。 exe”。

解析器所做的全部工作就是将命令的名称记为StringConstantExpressionAst,这取决于运行时逻辑来查找要运行的具有该名称的东西。

如果仔细查看AST,您会发现“描述”令牌的StringConstantType属性为BareWord,而"my tests"字符串的值为{{1} }。如果只想对“文字字符串”进行处理,则可以使用DoubleQuoted属性作为过滤器。

StringConstantType

除了然后,您可能会错过诸如此类的未加引号的字符串:

$Strings = $AST.FindAll(
    {
        ( $args[0] -is [System.Management.Automation.Language.StringConstantExpressionAst] ) -and
        ( $args[0].StringConstantType -ne "BareWord" )
    },
    $true
)

所以另一个更好的选择是忽略任何CommandAst节点中的第一个子元素。