Thanks to the PowerShell expression mode, PowerShell has some nice ways to de-serialize objects, like:
Invoke-Expression
cmdletInvoke-Command
cmdlet&
My general expectation is that the result of an given expression should be the same as using one of above listed de-serialize commands on same serialized version of that expression (for background, see the ConvertTo-Expression answer on the 'Save hash table in PowerShell object notation (PSON)' question).
In other words:
<Expression> <=> Invoke-Command {<Expression>} <=> &([ScriptBlock]::Create('<Expression>'))
<Expression> <=> Invoke-Expression '<Expression>'
Examples:
Get-ChildItem <=> &{Get-ChildItem}
Get-ChildItem <=> Invoke-Command {Get-ChildItem}
Get-ChildItem <=> Invoke-Expression 'Get-ChildItem'
1, 2, 3 <=> &{1, 2, 3}
1, 2, 3 <=> Invoke-Command {1, 2, 3}
1, 2, 3 <=> Invoke-Expression '1, 2, 3'
This indeed appears true for mainly every expression, but due to the fact that PowerShell unrolls (enumerates) the output by default, this definition deviates in the case an expression contain an array with a single item:
,1 <≠> Invoke-Command {,1}
,1 <≠> Invoke-Expression ',1'
,"Test" <≠> Invoke-Command {,"Test"}
,"Test" <≠> Invoke-Expression ',"Test"'
@("Test") <≠> Invoke-Command {@("Test")}
@("Test") <≠> Invoke-Expression '@("Test")'
,@("Test") <≠> Invoke-Command {,@("Test")}
,@("Test") <≠> Invoke-Expression ',@("Test")'
Is there a way to prevent that expressions get unrolled when the are invoked (de-serialized) in anyway?
I am considering to request for a -NoEnumerate
parameter (similar to the Write-Output
cmdlet) for the Invoke-Expression
on the PowerShell GitHub, but that will still leave the issue/question for the call operator and dot sourcing that do not support parameters...
I found a workaround to prevent the default unrolling of (serialized) expressions:
Wrap the expression in a HashTable:
<Expression> <=> (Invoke-Command {@{e=<Expression>}})['e']
<Expression> <=> (Invoke-Expression '@{e=<Expression>}')['e']
Examples:
Get-ChildItem <=> (Invoke-Command {@{e=Get-ChildItem}})['e']
Get-ChildItem <=> (Invoke-Expression '@{e=Get-ChildItem}')['e']
1, 2, 3 <=> (Invoke-Command {@{e=1, 2, 3}})['e']
1, 2, 3 <=> (Invoke-Expression '@{e=1, 2, 3}')['e']
,1 <=> (Invoke-Command {@{e=,1}})['e']
,1 <=> (Invoke-Expression '@{e=,1}')['e']
I have further implemented this in a ConvertFrom-Expression cmdlet, which the following features:
-NoEnumerate
switch to prevent arrays with a single item to unroll-NoNewScope
switch similar to Invoke-Command
-Expression
argumentConvertFrom-Expression
Examples:
PS C:>'2*3', {3*4}, '"Test"' | ConvertFrom-Expression
6
12
Test
PS C:> (ConvertFrom-Expression ',"Test"' -NoEnumerate) -Is [Array]
True