Skip to content

PSUseProcessBlockForPipelineCommand does not recognize use of $input within an End block #1912

Closed

Description

Steps to reproduce

Invoke-ScriptAnalyzer -IncludeRule PSUseProcessBlockForPipelineCommand `
	-ScriptDefinition 'Param([Parameter(ValueFromPipeline=$true)][psobject] $Item); End {$input |ConvertTo-Json}'

Expected behavior

(no output)

Actual behavior

RuleName                            Severity     ScriptName Line  Message
--------                            --------     ---------- ----  -------
PSUseProcessBlockForPipelineCommand Warning                 1     Command accepts pipeline input but has not defined a
                                                                  process block.

Environment data

> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.3.4
PSEdition                      Core
GitCommitId                    7.3.4
OS                             Microsoft Windows 10.0.22621
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0


> (Get-Module -ListAvailable PSScriptAnalyzer).Version | ForEach-Object { $_.ToString() }
1.21.0

Activity

  1. StevenBucher98 commented on May 9, 2023

    @StevenBucher98
    Collaborator

    This is by design, if you take a parameter that takes input from a pipeline then there is an expectation that the process block is going to handle it since it has to run multiple times. Begin and end blocks only run once in a pipeline. Its not complaining about $input but $Item because $Item needs to be handled in a process block.

  2. brianary commented on May 9, 2023

    @brianary
    Author

    Steven Bucher (@StevenBucher98) The $input variable is an automatic variable: "In the end block, the $input variable enumerates the collection of all input to the function."

    In the example above, $input includes all of the values of $Item. This is a pattern that can be used when all of the input needs to be gathered before using it, such as when using Write-Progress to display the percentage that has been completed after each item.

  3. StevenBucher98 commented on May 9, 2023

    @StevenBucher98
    Collaborator

    I think since you still have a parameter $Item the takes ValueFromPipeline it needs to be in a process block. I'd have to rely on James Truher (@JamesWTruher) for a deeper explanation or fix.

  4. brianary commented on May 9, 2023

    @brianary
    Author

    Steven Bucher (@StevenBucher98) There's more than one way to consume that pipeline data. Explicit usage in the Process block is certainly the most common way, but if you try to use that approach, it'll consume the data before you can use it in the End block, which is a legitimate alternative method for processing the data. You can't mix them.

  5. JamesWTruher commented on May 9, 2023

    @JamesWTruher
    Contributor

    Brian Lalonde (@brianary) using $input in the end is not something that we recommend and will likely lead to misunderstandings as the following example shows.

    function f1 {
        param ([parameter(ValueFromPipeline=$true)]$a)
        BEGIN   { "input value: '$input' `tinput count: $($input.count) `tvalue of a: '$a'" }
        PROCESS { "input value: '$input' `tinput count: $($input.count) `tvalue of a: '$a'" }
        END     { "input value: '$input' `tinput count: $($input.count) `tvalue of a: '$a'" }
    }

    when run, the output is as follows:

    PS> 1..3 | f1
    input value: '' 	input count: 0 	value of a: ''
    input value: '1' 	input count: 1 	value of a: '1'
    input value: '2' 	input count: 1 	value of a: '2'
    input value: '3' 	input count: 1 	value of a: '3'
    input value: '' 	input count: 0 	value of a: '3'

    As you can see, $input has no value in both the BEGIN and END blocks and a has the last value that $a was set to in the END block.

    This rule is expressing the appropriate expectation that if you have pipelined parameters, you need to handle them in a PROCESS block. The rule violation has nothing to do with $input in the END block, only that you have failed to handle the pipeline variable in the appropriate block.

  6. brianary commented on May 10, 2023

    @brianary
    Author

    James Truher (@JamesWTruher)
    Yes, you can't mix $input in multiple blocks, as I said, because it consumes the values.

    function f1 {
        param ([parameter(ValueFromPipeline=$true)]$a)
        END     { "input value: '$input' `tinput count: $($input.count) `tvalue of a: '$a'" }
    }
    input value: '1 2 3'    input count: 3  value of a: '3'
  7. ghost closed this as completedon May 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions