Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
brianary opened this issue May 7, 2023 · 6 comments

Comments

@brianary
Copy link

brianary commented May 7, 2023

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
@ghost ghost added the Needs: Triage 🔍 label May 7, 2023
@StevenBucher98
Copy link
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.

@brianary
Copy link
Author

brianary commented May 9, 2023

@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.

@StevenBucher98
Copy link
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 @JamesWTruher for a deeper explanation or fix.

@brianary
Copy link
Author

brianary commented May 9, 2023

@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.

@JamesWTruher
Copy link
Member

JamesWTruher commented May 9, 2023

@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.

@brianary
Copy link
Author

@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'

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants