powershell

Can't show the creation time for a file as the first column in PowerShell


I obtain a list of files and then attempt to show them. I only want the name and date to be displayed. I got it to work but realized that in order to align well, I'd like the data to go first and then the name. I don't want to align by putting those in separate columns. I definitely don't want to add logic controlling the number of spaces between name and time.

To my surprise, the two printouts work differently: first one as expected, second one all empty.

$list = Get-ChildItem "."

$list `
  | Sort-Object CreationTime `
  | Select-Object @{Name=" "; Expression={$_.BaseName + "   " + $_.CreationTime}}

$list `
  | Sort-Object CreationTime `
  | Select-Object @{Name=" "; Expression={$_.CreationTime + "   " + $_.BaseName}}

How can I explain this discrepancy?


Solution

  • Column Order

    You can use Format-Table to order the columns with aligned padding:

    PS> $list `
        | Sort-Object CreationTime `
        | Format-Table CreationTime, BaseName
    
    CreationTime        BaseName
    ------------        --------
    05/12/2024 09:54:42 Videos
    05/12/2024 09:54:42 Saved Games
    05/12/2024 09:54:42 OneDrive
    05/12/2024 09:54:42 Documents
    05/12/2024 09:54:42 Music
    05/12/2024 09:54:42 Links
    05/12/2024 09:54:42 Favorites
    ...
    

    And if you don't want the column headers you can add -HideTableHeaders:

    PS> $list `
        | Sort-Object CreationTime `
        | Format-Table CreationTime, BaseName -HideTableHeaders
    
    05/12/2024 09:54:42 Videos
    05/12/2024 09:54:42 Saved Games
    05/12/2024 09:54:42 OneDrive
    05/12/2024 09:54:42 Documents
    05/12/2024 09:54:42 Music
    05/12/2024 09:54:42 Links
    05/12/2024 09:54:42 Favorites
    ...
    

    If you specifically want to capture that output into a string (to write to a logfile for example) you can pipe it into Out-String:

    PS> $text = $list `
        | Sort-Object CreationTime `
        | Format-Table CreationTime, BaseName `
        | Out-String
    
    PS> $text
    
    CreationTime        BaseName
    ------------        --------
    05/12/2024 09:54:42 Videos
    05/12/2024 09:54:42 Saved Games
    05/12/2024 09:54:42 OneDrive
    05/12/2024 09:54:42 Documents
    05/12/2024 09:54:42 Music
    05/12/2024 09:54:42 Links
    05/12/2024 09:54:42 Favorites
    ...
    

    Blank Values

    You're seeing blank values in your second sample because of this:

    PS>  "aaa" + [datetime]::Now
    aaa03/13/2025 12:32:57
    
    PS> > [datetime]::Now + "aaa"
    MethodException: Cannot convert argument "1", with value: "aaa",
    for "op_Addition" to type "System.TimeSpan": "Cannot convert value "aaa" to type
    "System.TimeSpan". Error: "String 'aaa' was not recognized as a valid TimeSpan.""
    

    Basically, you can add a [string] and a [DateTime] together because PowerShell uses the left-hand side's (i.e. [string]'s) op_addition operator and tries to convert the [DateTime] to a type supported by that before it performs the addition, so it converts the [DateTime] to a string first.

    However, when you try to add a [DateTime] and a [string] PowerShell uses [DateTime]'s op_addition operator and tries to convert the string to a type supported by that, but there's no conversion available so an exception gets thrown instead.

    That exception means no result is returned from the Expression={$_.BaseName + " " + $_.CreationTime} so no value is shown for that item in your output.

    If you're wedded to the Select-Object approach you can convert the date to a string yourself before the addition:

    $list `
        | Sort-Object CreationTime `
        | Select-Object @{Name=" "; Expression={$_.CreationTime.ToString() + "   " + $_.BaseName}}
    
    
    -
    05/12/2024 09:54:42   Videos
    05/12/2024 09:54:42   Saved Games
    05/12/2024 09:54:42   OneDrive
    05/12/2024 09:54:42   Documents
    05/12/2024 09:54:42   Music
    05/12/2024 09:54:42   Links
    05/12/2024 09:54:42   Favorites