PowerShell 是否要求脚本块的左花括号在同一行上?

zzx*_*xyz 4 syntax powershell

好的,这个脚本的预期“输出”是相当明显的,它可以工作。它复制文件:

Get-ChildItem -Recurse -Directory | ForEach-Object{
    if ($_.Name.ToUpper().Contains("INCLUDE_PUBLIC")){
        Get-ChildItem -Recurse -File -Include "*.h" -Path $_.FullName | ForEach-Object {Copy-Item ($_.FullName) ("e:\IncTest")}
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我将左大括号{移到下一行,则会得到以下输出:

cmdlet ForEach-Object at command pipeline position 2
Supply values for the following parameters:
Process[0]: 
Run Code Online (Sandbox Code Playgroud)

我是否被其他一些语法问题欺骗了,或者 ForEach-Object 是否要求它的大括号在同一行上?

mkl*_*nt0 5

PowerShell 有两种不同的解析模式,因为它既是shell又是脚本语言

  • 参数模式是面向行的,类似于 shell:它适用于调用可执行文件 / 脚本 / cmdlet / 别名 / 函数的单个命令,或一系列用管道符号 (|)链接以形成管道的此类命令。

    • 每个单独的命令都必须在自己的行上,将它分散到多行的唯一方法是使用line continuation,这需要使用`(反引号)转义每个内部行的末尾。
      然而,这种做法在视觉上既微妙又脆弱(在`中断命令后甚至只放置空格或制表符)。

    • 为了将管道的各个命令分散到多行(而不必使用行延续),请以|.

  • 表达式模式的工作方式在空格不重要的其他编程语言中一样:只要构造在语法上是完整的,它就可以跨越任意数量的行

需要注意的是,单个命令可能涉及多种解析模式,例如当您将脚本块传递给 cmdlet 时,cmdlet 以参数模式解析其参数,但脚本块本身以表达式模式解析(请参阅以下)。

有关解析模式的官方文档位于概念about_Parsing帮助主题中


ForEach-Object是一个cmdlet,因此在参数模式下解析。

因此,除非您使用行继续,否则它不会在后续行中查找参数,并且ForEach-Object在没有参数的情况下自行执行,会导致它提示输入必需的参数 --Process脚本块 - 这就是您所看到的。

相比之下,... | ForEach-Object {尽管脚本块在后续行中继续运行,但仍然有效,因为脚本块本身在表达式模式下被解析,允许它分布在多行中,而在同一行上启动脚本块 - 开头{- 就ForEach-Object足够了为了ForEach-Object认出它。


将在参数模式下解析的cmdlet 与在表达式模式[1]解析的语句进行对比的示例 :ForEach-Objectforeach

# Argument mode 
1, 2 | ForEach-Object { # opening brace must be on same line
  "Element: $_" 
}


# Expression mode
foreach ($el in 1, 2) # expression mode - OK to place opening { on next line
{
  "Element: $el" 
}
Run Code Online (Sandbox Code Playgroud)

[1]需要注意的是,也许是容易混淆,ForEach-Object有一个别名也称foreach,这模糊了cmdlet和区分foreach 声明。情境解析模式决定了foreach是被解释为别名(并因此引用ForEach-Object)还是被解释为语句。


The*_*le1 3

PS C:\> Get-Help -Name 'ForEach-Object'

SYNTAX
    ForEach-Object [-MemberName] <String> [-ArgumentList <Object[]>] [-Confirm] [-InputObject <PSObject>] [-WhatIf] [<CommonParameters>]

    ForEach-Object [-Process] <ScriptBlock[]> [-Begin <ScriptBlock>] [-Confirm] [-End <ScriptBlock>] [-InputObject <PSObject>] [-RemainingScripts <ScriptBlock[]>] [-WhatIf] [<CommonParameters>]
Run Code Online (Sandbox Code Playgroud)

通过使用脚本块,您可以使用 cmdlet 的位置绑定参数ForEach-Object。在本例中,-Process { }参数设置。如果您转义行尾(本质上是转义换行符),您可以解决这个问题,但这通常是一个不好的做法。