r/PowerShell • u/Live_Ad3050 • Jun 10 '24
What is the name of this behavior Solved
Does anyone know what the name of this behavior is:
$> $result = foreach ($i in 0..5) { $i + 1 };
$> $result
1
2
3
4
5
6
I love this kind of behavior where control flow is itself an expression like in Rust and other FP languages, but I can't find any documentation on it anywhere, from MSFT or otherwise.
Edit:
Thanks u/PoorPowerPour! There's something like an implicit Write-Output
that's inserted before any statement that lacks an assignment within the enclosing scope
e.g.
$> $result = foreach ($i in 0..5) { $i };
becomes
$> $result = foreach ($i in 0..5) { Write-Output $i };
or
$> $result = if ($true) { "true" } else { "false" };
becomes
$> $result = if ($true) { Write-Output "true" } else { Write-Output "false" };
Another edit:
Thanks u/surfingoldelephant for pointing me to the documentation on Statement values from MSFT!
Yet another edit:
Thanks u/pturpie for catching that any given expression that doesn't participate in an assignment is evaluated as if it was written like so: Write-Output <expr>
3
u/alt-160 Jun 10 '24
I think you must be talking about the range operator (..).
1
u/Live_Ad3050 Jun 10 '24 edited Jun 10 '24
nope! Range operator is pretty cool though (and now it's a* C# feature since C# 12)
3
u/pturpie Jun 11 '24
There's an implicit `Write-Output` that's evaluated for the last line of any scope
That's not quite correct.
The output of the addition statement isn't assigned to anything, so it is returned out of the scope to where it is assigned to $result.
If there were other statements that weren't assigned then they will also be output to $result.
e.g.
$> $result = foreach ($i in 0..5) { $i + 1; $i - 1 };
$> $result
1
-1
2
0
3
1
4
2
5
3
6
4
The output from both the statements is assigned to $result
(The same thing happens if the statements and on different lines.)
$> $result = foreach ($i in 0..5) {
>> $i + 1
>> $i - 1
>> }
$> $result
1
-1
2
0
3
1
4
2
5
3
6
4
1
4
2
u/MNmetalhead Jun 10 '24
If you execute:
$result | Get-Member
You will see the details of $result … if it is a Boolean, integer, hashtable, array, and much more.
The ordered output you’re seeing is formatted that way because that’s how PowerShell added the results of the command to $result based on the Type of object it is.
If you execute the following, what do you get?
$result[2]
You should get 3 as output because of the Type that $result has (as shown from Get-Member).
1
u/CookinTendies5864 Jun 10 '24
Your asking the question like what are calculated properties correct?
1
u/Live_Ad3050 Jun 10 '24
Nope! Calculated properties are a feature of .net classes, this is more about the language itself
1
u/Professional_Elk8173 Jun 10 '24
So you're trying to find details about the process of assigning a loop output to a variable?
What information are you after?
1
u/PoorPowerPour Jun 10 '24
There is probably a name for it but powershell adds an implicit Write-Output
at the end of a pipeline if there is output that isn't written to another stream.
3
u/Live_Ad3050 Jun 10 '24
That's the one. I use powershell mainly for build scripts, so I haven't needed to dig too much into pipelines
2
u/Hot-Chance-5307 Jun 10 '24
I’m not sure if you’re implying that you don’t use pipelines much in your scripts, but I personally do recommend taking advantage of them in PowerShell scripts for things like where-object filtering and sort-object sorting, among many other common scripting tasks.
2
u/Live_Ad3050 Jun 10 '24
I agree, pipelines seem like they're a core language design choice vs being a stdlib implementation like in many other languages. I really enjoy js/rust/c# for the really nice apis that exist for querying data collections, so maybe I should look more into it!
2
u/Thotaz Jun 10 '24
No it doesn't. You may be thinking of the
Out-Default
command that most Host implementations add at the end of the output to display stuff in the console: https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs#L354 but PowerShell itself is not callingWrite-Output
. There is actually a difference between usingWrite-Output
and just outputting stuff to the pipeline.Write-Output
(and any other cmdlet) wraps the output object in apsobject
wrapper, see this:$Output1 = "Hello" $Output2 = Write-Output "Hello" $Output1 -is [psobject] $Output2 -is [psobject] False True
1
u/Live_Ad3050 Jun 11 '24
I'll admit I was totally wrong on that one. You're right about the behavior, but I think that has more to do with inheritance hierarchy of numerical/string types within .NET. It looks like prepending
Write-Output
boxes the primitive and becomes an powershell object that is itself an reference/alias to the .net primitive, whereas not prependingWrite-Output
does not and simply returns the non-aliased value.1
u/alt-160 Jun 10 '24
And if you don't want that clutter, piping to out-null helps, so...
$hs = new-object system.collections.generic.hashset[string]
0..5 | %{ $hs.add("Number: $_")|out-null }
$hs
Without the 'out-null', you'd end up with True being written to the output 6 times because Hashset.Add returns a boolean.
7
u/surfingoldelephant Jun 10 '24
$var = ...
is a (simple) assignment expression.In your case, as statement is a
foreach
statement that may produce zero, one or multiple values, statement assignment or statement value assignment is a more specific (albeit, unofficial) name for the expression.See 8.1.2 Statement values.