I was trying to print out environment variables in alphabetical order (by name), and the first thing I could come up with was:
Get-Item env: | Sort-Object -Property Name
But the output was always unsorted. Then I tried the following
Get-ChildItem env: | Sort-Object -Property Name
And got the expected, correctly sorted, output. This suggests that the output of Get-Item and Get-ChildItem is not of same type, even though the (formatted) output from Get-Item env: and Get-ChildItem env: looks precisely the same (i.e. dictionary)
Piping the output from the commands to | Get-TypeData revealed that Get-Item env: seems to actually return just single System.Collections.DictionaryEntry, whereas Get-ChildItem env: returns multiple System.Collections.DictionaryEntry objects.
Can someone explain what is precisely happening here? Why two seemingly different input data types produce identical looking output/string representation? Is PowerShell doing some implicit "unboxing" of the single-entry dictionary object from Get-Item output?
Using PowerShell 5.1 on Windows 10.
tl;dr:
Get-Itemisn't worth using with theenv:drive's root; to enumerate the current process' environment variables, useGet-ChildItem env:instead.Generally, use
Get-Itemto get information about the targeted item itself, andGet-ChildItemto get information about its children.Get-Itemis designed to return a given item itself, whereasGet-ChildItemreturns its children.Note:
Get-ChildItemfalls back to the item itself for items that by definition cannot have children, such as individual environment variables or files - see bottom section.Get-Item env:is akin toGet-Item C:\in that you're asking for the root of a PowerShell drive itself, not its children.env:is the PowerShell drive that contains all environment variables defined in the current process, and as itself it currently has a representation of limited utility, only accessing its children works as expected. (Contrast this with a root directory such asC:\, which has meaningful properties itself, such as timestamps, permissions, ...)What PowerShell returns for the
env:drive is the collection of entries from the dictionary it uses to store information about the individual environment variables as a single object[1], which is usual behavior, in that commands are normally expected to send a collection's elements to the pipeline, one by one. That information about the item itself effectively includes the children too is unusual as well.It is a moot point, because
Get-ChildItem env:will give you the same functionality in a conceptually clearer manner, but you could use(...), the grouping operator to force enumeration of the items in the collection thatGet-Item env:outputs:What would arguably more sense if PowerShell returned the whole dictionary rather than its collection of entries, so that you could access
.Keysto get all environment-variable names and.Valuesto get all values. (Dictionaries / hashtables are not expected to be enumerated in PowerShell pipelines).In fact, due to member-access enumeration, you could achieve the same effect by accessing properties
.Keyand.Valueon the entry collection currently returned byGet-Item env:"Sloppy" use of
Get-ChildItemAs stated, for item types that by definition cannot have child items,
Get-ChildItemfalls back toGet-Itembehavior, so that the following two commands are effectively equivalent:However, it is conceptually preferable to use
Get-Itemin such situations, given that it expresses the intent unambiguously.As an aside: the commonly used
$env:PATHsyntax for directly retrieving a given environment variable's value is an instance of namespace variable notation and is the equivalent ofGet-Content env:PATH(notGet-Item).[1]
Get-Item env:returns the.Valuesproperty value of theSystem.Collections.Generic.Dictionary`2instance that PowerShell uses to store information about environment variables. That value is output as a single object, and its type is a collection type nested inside the dictionary type,System.Collections.Generic.Dictionary`2.ValueCollection; you can inspect the type withGet-Item env: | Get-Member