3

Good morning peeps,

Im a big fan of making a selection from values that are returned from listed items. So, I was expirementing with making a selection out of the values returned from C:\Users, which is input into a [PSCustomObject] type, but noticed something off. I can list the selection into the [PSCustomObject] just fine like so:

[array]$Userlist = Get-ChildItem C:\users | Sort-Object -Property LastWriteTime -Descending
        for($i=0; $i -lt $UserList.BaseName.count; $i++){
        [PSCustomObject]@{
                'Profile Name'  = "$($i): $($UserList.BaseName[$i])"
                '  Full Path  ' = $UserList.FullName[$i] 
                'Modified Time' = $UserList.LastWriteTime[$i] 
                        }
                    }

#Output:
Profile Name   Full Path      Modified Time        
------------ -------------    -------------        
0: Abraham   C:\users\Abraham 4/11/2021 10:26:58 PM
1: Public    C:\users\Public  3/28/2021 8:51:28 AM 

..but, when I try to make a selection simply by adding a Read-Host at the end of the script, I get that prompt first:

[array]$Userlist = Get-ChildItem C:\users | Sort-Object -Property LastWriteTime -Descending
        for($i=0; $i -lt $UserList.BaseName.count; $i++){
        [PSCustomObject]@{
                'Profile Name'  = "$($i): $($UserList.BaseName[$i])"
                '  Full Path  ' = $UserList.FullName[$i] 
                'Modified Time' = $UserList.LastWriteTime[$i] 
                        }
                    }
                
              
$ii = Read-Host -Prompt "Enter The Users Number to Delete"
$i  = $ii -split " "
      ""
    foreach($profile in $Userlist.baseName[$i]){
        ""
        "Selection: $profile"
        }

#output
Enter The Users Number to Delete: 1    <------ Here its asking first before displaying.
Profile Name   Full Path      Modified Time        
------------ -------------    -------------        
0: Abraham   C:\users\Abraham 4/11/2021 10:26:58 PM
1: Public    C:\users\Public  3/28/2021 8:51:28 AM 


Selection: Public

Am I missing something? Why is my Read-Host being prompted before my top object is displayed? Id like to see the selection before i choose lol

Is there an order in which it's displayed?

Abraham Zinala
  • 4,267
  • 3
  • 9
  • 24
  • Would I have to assign my object to a variable then have it displayed before the prompt? – Abraham Zinala Apr 17 '21 at 16:09
  • nvm, just tried that and it still didnt work. – Abraham Zinala Apr 17 '21 at 16:10
  • i understand that it shoots to out-default first. Then, out-host should come after but, its not like the object itself is being consumed, or is it? – Abraham Zinala Apr 17 '21 at 16:21
  • hmm, interesting topic. My work around at the moment is assign the output to a variable, and then pipe it to `out-host`. So: `$Obj = for($i=0; $i -lt.....`. Then `$obj | out-host`. Just confused on how `Read-Host` goes to the screen before the object does. Even tho, there should be an `Out-Host` already there? maybe im thinking too much into it. – Abraham Zinala Apr 17 '21 at 16:25
  • 1
    So this is my best explanation of how I understand it. The entire script is parsed before anything is ran. Any -Host goes straight to the powershell formatter to be output. The rest goes through the powershell pipeline before finally getting sent to the formatting/output system. If you add Out-Host it will actually make it to the formatting system first and therefore be in the correct order. That's why even adding a sleep command doesn't help. You can also just surround an entire code block with parenthesis and pipe that to out-host instead of assigning to a variable. – Doug Maurer Apr 17 '21 at 16:32
  • The lack of synchronization between pipeline output and other output streams is limited to PS v5+ and a very specific - albeit still common - scenario: implicitly _table_-formatted output for types that do _not_ have formatting data defined for them. The - suboptimal - workaround is to force the pipeline output synchronously to the host (display) with `Out-Host` - see [this answer](https://stackoverflow.com/a/43691123/45375) to the linked duplicate. – mklement0 Apr 17 '21 at 17:01
  • Check Ordered hashtables: https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-hashtable?view=powershell-7.3#ordered-hashtables – lk7777 Nov 11 '22 at 20:25
  • @lk7777, unfortunately, that wasn't what I was after. – Abraham Zinala Nov 14 '22 at 16:13

1 Answers1

1

A similar example of how the formatting system can surprise users.

Have a simple CSV

@'
OneProperty
test
'@ | ConvertFrom-Csv

OneProperty
-----------
test  

And another

@'
OneProperty,TwoProperty
test1,test2
'@ | ConvertFrom-Csv

OneProperty TwoProperty
----------- -----------
test1       test2  

Everything is perfect, as expected.

But running these together

@'
OneProperty
test
'@ | ConvertFrom-Csv

@'
OneProperty,TwoProperty
test1,test2
'@ | ConvertFrom-Csv

OneProperty
-----------
test       
test1      

Where is the second property? Well, the first object to hit the formatting system is what determines the properties. Powershell won't check each item because that could be a big performance hit. However, if you force the output to the formatter with Out-Host, Out-Default, or any of the Format-* cmdlets..

@'
OneProperty
test
'@ | ConvertFrom-Csv | Out-Default

@'
OneProperty,TwoProperty
test1,test2
'@ | ConvertFrom-Csv | Out-Default


OneProperty
-----------
test       


OneProperty TwoProperty
----------- -----------
test1       test2   

We can see both are shown completely. Simply put, the Read-Host cmdlet just beats your pipeline output to the formatting system.

You can also surround commands/sections of code with sub-expression and pipe it versus assigning to a variable.

$(for($i=0; $i -lt $UserList.BaseName.count; $i++){
    [PSCustomObject]@{
        'Profile Name'  = "$($i): $($UserList.BaseName[$i])"
        '  Full Path  ' = $UserList.FullName[$i] 
        'Modified Time' = $UserList.LastWriteTime[$i] 
    }
}) | Out-Default
Doug Maurer
  • 8,090
  • 3
  • 12
  • 13
  • I think I'm following along but, how does the sub-expression operator allow it to take precedence? So the when the script is executed, it's analyzed entirely instead of a cmdlet at a time? I think that makes sense due to it giving you an error if you're missing a closing statement, or something a long those lines. This post is good enough for me! Although, I'd like to dig deeper on why it waits, wouldn't that mean every other psobject would wait till the end to be displayed? – Abraham Zinala Apr 17 '21 at 16:52
  • So, say you have some like this: `[psobject,] here, read-host "something";[anotherhere] here2 Read-Host "something else"`. Doesn't so all the read hosts will executed first? – Abraham Zinala Apr 17 '21 at 16:55
  • 2
    "Simply put, the Read-Host cmdlet just beats your pipeline output to the formatting system" - that is the crux of the problem, and happens only in very specific - albeit still common - circumstances - see [this answer](https://stackoverflow.com/a/43691123/45375). The other problem you describe - "disappearing" properties - is also common, but incidental to the question; it is discussed in depth in [this answer](https://stackoverflow.com/a/45705068/45375). – mklement0 Apr 17 '21 at 17:07
  • Gonna read on it now, thank you! – Abraham Zinala Apr 17 '21 at 17:11