$script:Num = 0
function Alert
{
param($Name)
Write-Host "${Name}: $script:Num"
[void]$script:Num++
}
function Do($Val)
{
Write-Output $Val
Alert "Write"
}
function Process
{
param([Parameter(ValueFromPipeline)]$V)
process
{
foreach ($val in $V)
{
Alert "Process"
}
}
}
$null = Invoke-Expression "(Do 1 && Do 2) && Do 3" | Process
Write: 0
Write: 1
Write: 2
Process: 3
Process: 4
Process: 5
Write: 0
Write: 1
Process: 2
Process: 3
Process: 4
Write: 5
Performing this without Invoke-Expression you get a different result:
((Do 1 && Do 2) && Do 3) | Process
outputs:
Write: 0
Write: 1
Write: 2
Process: 3
Process: 4
Process: 5
Interesting:
$null = Invoke-Expression "(Do 1) && (Do 2 && Do 3)" | Process
Write: 0
Process: 1
Write: 2
Write: 3
Process: 4
Process: 5
I suspect this might actually be by design. It's quite specific to Invoke-Expression. Here's a repro without pipeline chains:
$i = 0; function process { process { $_; $i } }; Invoke-Expression '. { "a"; $i++; "b" }' | process
Outputs:
a
0
b
1
this might actually be by design
Maybe @mklement0 can add more info about Write-Host. It seems we have some related issues.
I think these are related but slightly different. @JamesWTruher saw a similar thing with Start/Stop-Transcript recently. In that case (and in the case of Write-Host I suspect), what's written to the pipeline always occurs in a clear order, but what's written to the host depends on formatting and stringification. So interleaving Write-Output with Write-Host for example, can print all the Write-Host output first in some cases, since Write-Host immediately displays the input when called, whereas Write-Output writes to the pipeline and the output is only displayed when that pipeline's contents hit an Out-Default that writes to the console.
In this case however, there's actual interleaving of pipeline writing from within and without Invoke-Expression. Basically Invoke-Expression is not an eager invocation, but instead creates a steppable iterator where the downstream pipe consumes the output on demand.
instead creates a steppable iterator where the downstream pipe consumes the output on demand.
In this case, it is hardly possible to correct this output and probably not worth it.
Perhaps this will be a problem if there are really big expressions in a script in interactive session alternating with the Write-Host output.
Workaround is simple :smile:
Invoke-Expression "(Do 1 && (Do 2 && Do 3))" | Process
Closing this as by design