I have been spending some time trying to get my head around the pipe line specifically when passing collections across it and which sections of code get executed when [Begin, Process, End]. Please feel free to explain that to me. But my actual problem is something that I have come across when researching the above.
If I define a multi line string in either the begin or process block I can no longer debug my code. And by that I mean that I can not step through the assignment of this string. If I F5 past the assignment to a later break point all is well but trying to step through the string assignment always throws the following exception which completely kills my session:
Specified argument was out of the range of valid values.
Parameter name: length
At C:\Temp\Test.ps1:11 char:9
function Test {
[CmdletBinding(DefaultParameterSetName='Parameter Set 1',
SupportsShouldProcess=$true,
PositionalBinding=$false,
HelpUri = 'http://www.microsoft.com/',
ConfirmImpact='Medium')]
Param ( )
Begin {
$y=''
$x="
boo
"
}
Process {
if ($pscmdlet.ShouldProcess("Target", "Operation")) {
$x
}
}
End {
}
}
Test
My “Toolmaking” book does a pretty good job of explaining the lifecycle. But, in general:
When a function is run without pipeline input, BEGIN/PROCESS/END are basically ignored and the code within them runs top to bottom as if they didn’t exist.
When a function is run with pipeline input, BEGIN runs first, then PROCESS runs and is handed one input object at a time, and then END runs when they’re all done.
For multi-line strings, you should be using a here-string:
Agreed its a Ctrl +J default. Don I previously attempted to use the here-string and got the same results. The ISE does not seem to like stepping through multi line string assignment.
Stephen, here is some sample code to show how powershell processes the begin, process and end blocks with pipeline and non-pipeline input. What you will see is that, as Don mentions, it run begin first, then process for each object and then end. What might not be clear is that PowerShell runs the Begin block for every cmdlet/function in the pipeline before running the begin for any other cmdlet/function as shown in the example below. So if you are expecting to use an input object in the begin block of your function, it will not work.
Function Function1 {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipelineByPropertyName=$True)]
[string]$message
)
Begin {Write-Verbose "Begin Function1: $message"}
Process {
Write-Verbose "Process Function1: $message"
[PSCustomObject]@{
message = $message
}
}
End {Write-Verbose "End Function1: $message"}
}
Function Function2 {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipelineByPropertyName=$True)]
[string]$message
)
Begin {Write-Verbose "Begin Function2: $message"}
Process {
Write-Verbose "Process Function2: $message"
[PSCustomObject]@{
message = $message
}
}
End {Write-Verbose "End Function2: $message"}
}
Test Function1
PS C:\> function1 -message "Test" -verbose
VERBOSE: Begin Function1: Test
VERBOSE: Process Function1: Test
message
Test
VERBOSE: End Function1: Test
Test Function2
PS C:\> function2 -message "Test" -verbose
VERBOSE: Begin Function2: Test
VERBOSE: Process Function2: Test
message
Test
VERBOSE: End Function2: Test
Test Function1 Pipe to Function2
PS C:\> function1 -message "Test" -verbose | function2 -Verbose
VERBOSE: Begin Function1: Test
VERBOSE: Begin Function2:
VERBOSE: Process Function1: Test
VERBOSE: Process Function2: Test
message
Test
VERBOSE: End Function1: Test
VERBOSE: End Function2: Test
Thanks everyone. The pipeline while mysterious now makes a lot more sense. Or at least I now know the order in which the distinct script blocks execute.
What I’m still not sure about is whether the ISE has a bug regarding the debugging of code where there is a multi line string declaration or whether I have a really strange edge case. Has anyone come across this issue in their code? If you have time the code sample I provided will reproduce teh problem.