Does PowerShell require the opening curly brace of a script block to be on the same line?

前端 未结 2 1569
一个人的身影
一个人的身影 2021-01-13 20:25

Okay, the expected \"output\" of this script is fairly obvious, and it works. It copies files:

Get-ChildItem -Recurse -Directory | ForEach-Object{
    if ($         


        
相关标签:
2条回答
  • 2021-01-13 20:52
    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>]
    

    By using a script block, you're using positionally-bound parameters of the ForEach-Object cmdlet. In this case, the -Process { } parameter set. If you escape the end of the line (in essence, escaping the newline), you can get around that, but it's generally a bad practice.

    0 讨论(0)
  • 2021-01-13 21:01

    PowerShell has two distinct parsing modes, owing to the fact that it is both a shell and a scripting language.

    • Argument mode is line-oriented and is shell-like: It applies to a single command invoking an executable / script / cmdlet / alias / function, or a series of such commands chained with the pipe symbol (|) to form a pipeline.

      • Each individual command must be on its own line and the only way to spread it across multiple lines is to use line continuation, which requires escaping the very end of each interior line with ` (a backtick).
        However, that practice is both visually subtle and brittle (placing even just spaces or tabs after the ` breaks the command).

      • In order to spread the individual commands of a pipeline across multiple lines (without having to use line continuation), end each line but the last with |.

    • Expression mode works as it does in other programming languages in which whitespace isn't significant: as long as the construct is syntactically complete, it can straddle any number of lines.

    It's important to note that a single command can involve a mix of parsing modes, such as when you pass a script block to a cmdlet, where the cmdlet parses its arguments in argument mode, but the script block itself is parsed in expression mode (see below).

    The official documentation on parsing modes is in the conceptual about_Parsing help topic.


    ForEach-Object is a cmdlet and therefore parsed in argument mode.

    Thus, unless you use line continuation, it will not look for arguments on subsequent lines, and executing ForEach-Object on its own, without arguments, causes it to prompt for those of its arguments that are mandatory - the -Process script block - which is what you saw.

    By contrast, ... | ForEach-Object { works, despite the script block continuing on subsequent lines, because the script block itself is parsed in expression mode, allowing it to be spread across multiple lines, whereas starting the script block - the opening { - on the same line as ForEach-Object is sufficient for ForEach-Object to recognize it.


    An example that contrasts cmdlet ForEach-Object, parsed in argument mode, with the foreach statement, parsed in expression mode[1] :

    # 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" 
    }
    

    [1] Note that, perhaps confusingly, ForEach-Object has an alias also named foreach, which blurs the distinction between the cmdlet and the foreach statement. It is the situational parsing mode that determines wether foreach is interpreted as the alias (and therefore refers to ForEach-Object) or as the statement.

    0 讨论(0)
提交回复
热议问题