Search code examples
powershellobjectoutputtext-fileshashtable

Formatting a text file with columns in powershell


I am trying to create an nicely formatted Txt file (has to be Txt cannot be CSV) from a hashtable.

Here is the code that I have that creates the text file

$hashTable.GetEnumerator() | Sort-Object Name |
ForEach-Object {"{0} `t`t: {1} `t`t: {2} `t`t: {3} `t`t: {4}" -f $_.DisplayName,$_.EmployeeNo,$_.Email, $_.SamName,$_.Upn}|
Add-Content $textFilePath

As you can see - when someone is input into the Txt file with a long name it messes up the formatting because the tabs just get shifted along. enter image description here

I'm looking for a solution that dynamically sets the width of the columns in a text file.


Solution

  • Use PowerShell's output-formatting system, whose Format-Table cmdlet produces human-readable, column-aligned representations, but note that these are generally not suitable for later programmatic processing:

    ($hashTable.GetEnumerator() | Sort-Object Name).Value | 
      Format-Table -AutoSize -Property DisplayName, EmployeeNo, Email, SamName, Upn | 
        Out-File $textFilePath -Encoding Utf8 -Width 1024 
    
    • $hashTable.GetEnumerator() | Sort-Object Name enumerates the hashtable entries as System.Collections.DictionaryEntry entries and sorts them by their .Name (.Key) property.

      • Note that the .GetEnumerator() call is necessary to sort the hash table entries by their key (name); without it, the hash table as a whole would be sent as a single object through the pipeline, in which case the Sort-Object would effectively be a no-op - by definition, there's nothing to sort if there's only one object; compare the output from:
        @{ zach = 1; moe = 2; aardvark = 0 } | Sort-Object Name
        to the output from:
        @{ zach = 1; moe = 2; aardvark = 0 }.GetEnumerator() | Sort-Object Name
        In the first command, the Sort-Object call has no effect.
    • .Value then extracts the entries' values (this could also be done in streaming fashion with ... | ForEach-Object Value).

    • The values appears to be Active Directory user objects, so you can just pass a list of the desired property names to Format-Table, which form the output-table column names, whereas the corresponding property values form the column values.

    • Note that Out-File rather than Add-Content (Set-Content) is used, because only it automatically converts the formatting objects that Format-Table outputs to their intended string representations, as you would see in the console (terminal).

      • -Width 1024 ensures that the table lines aren't truncated based on whatever the width of the current console window happens to be; adjust as needed.
      • -Encoding Utf8 is used as an example to control the output encoding; note that Windows PowerShell defaults to "Unicode" (UTF-16LE), whereas the default is BOM-less UTF-8 in PowerShell [Core] v6+. See this answer for more information about default character encodings in Windows PowerShell vs. PowerShell [Core].