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

Why are there so many StringConstantExpressionAst's in this PowerShell script?

我正在对某些 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 可视化摘录:

请注意,图像右侧面板中突出显示的部分对应于左侧面板中的 AST 节点 CommandAST,然后有很多 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 行为的任何帮助。

PowerShell 是一种解释型语言,这意味着在您 运行 之前,它不会为代码的某些部分赋予意义。在你的例子中,它不知道 "Describe" 这个词指的是 Pester 模块中的 Describe 函数(它甚至可能还没有导入到你的会话中),它同样可能意味着一个名为 "Describe.exe" 例如。

解析器所做的只是将命令的名称记录为 StringConstantExpressionAst,然后由 运行时间逻辑来寻找 运行 的内容有那个名字。

如果您仔细查看您的 AST,您会发现 "Describe" 标记具有 BareWordStringConstantType 属性 而 "my tests" 字符串值为 DoubleQuoted。如果您只想对 "literal strings" 进行处理,您可以使用 StringConstantType 属性 作为过滤器。

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

除了 然后 你可能会错过像这样的未加引号的字符串:

Describe Files -Tag OSX,Linux {

所以另一个(更好的?)选项可能是忽略任何 CommandAst 节点中的第一个子元素。