In PowerShell how to display filename in the output of compare-object

500 Views Asked by At

I am very new to PowerShell scripting.

I have two folders with hundreds of files with the same name in both folders. I am writing a long script to compare all the files and send output to a text file. When I use this script:

compare-object  (get-content "D:\Folder1\file1.js")  (get-content "D:\Folder2\file1.js")
compare-object  (get-content "D:\Folder1\file2.js")  (get-content "D:\Folder2\file2.js")
compare-object  (get-content "D:\Folder1\file3.js")  (get-content "D:\Folder2\file3.js")

All I get is this:

InputObject SideIndicator
----------- -------------

     * New Text =>
     *          <=

This doesn't tell me which file has this difference. I want it to display the file name also (anyone, first or second file name).

How can I achieve that?

I know I should be using Get-Children, but I need to learn the simple thing first which can be implemented in a loop.

========= Solution Posted As a Separate Answer ================

2

There are 2 best solutions below

0
AnR On BEST ANSWER

With kind help of participants I have managed to create the following script that might be helpful for others:

$ErrorActionPreference = 'Continue'
$output = "result.txt"
del $output
get-Date >> $output
Get-ChildItem -Recurse -Path 'D:\Compare\Source\' -Include @('*.js','*.txt', '*.html', '*.css', '*.mxml') |
Foreach-Object {

    $f1 = $_.FullName
    $f2 = $f1.replace("\Source\","\Comparewith\")
    echo "`n" >> $output
    echo  $f2 >> $output
    Test-Path -Path $f2 -PathType Leaf >> $output
    compare-object  (get-content $f1) (get-content $f2) >> $output
}
get-Date >> $output

Basically I have two folders. Source and Comparewith. I am picking up each file from the Source folder as $f1, creating a similar file name $f2 while replacing Source with Comparewith but keeping same file name, and then comparing both. I am using echo to print the name before each compare-object and also testing if the file exists in the Comparewith folder.

Any improvement suggestion are welcome.

0
mklement0 On

Note:

  • This answer does not address what the OP was ultimately looking for.

  • Instead, it shows how to add a column to Compare-Object's output that shows for each difference object (line) the full path of the file that the line is exclusive too. Perhaps it is useful to some future readers.


You can take advantage of the fact that Get-Content decorates its output lines with a .PSPath property (among others) that contains the source file's full path:

Compare-Object (Get-Content D:\Folder1\file1.js) (Get-Content D:\Folder2\file1.js) | 
  Select-Object InputObject, 
                SideIndicator, 
                @{ n = 'Path'; e = { $_.InputObject.PSPath } }

Note the use of a calculated property to add a Path property to the output objects.

Select-Object creates new objects with the specified properties.
An alternative - for display only, if you additionally need to preserve the original output objects with, say, Compare-Object ... -OutVariable results - is to use Format-Table.


If, for any block of difference lines from the same file, you only want to print the path for the first line in the block, more work is needed, using an aux. variable in the caller's scope:

$prevPath = ''
Compare-Object (Get-Content D:\Folder1\file1.js) (Get-Content D:\Folder2\file1.js) | 
  Select-Object InputObject, 
                SideIndicator, 
                @{ 
                  Name = 'Path'
                  Expression = { 
                    if ($_.InputObject.PSPath -ne $prevPath) { 
                      (([ref] $prevPath).Value =  $_.InputObject.PSPath)
                    }
                  } 
                } 

Note:

  • The Expression script block runs in a child scope of the caller, so directly assigning to $prevPath would create a local variable that goes out of scope after every script block invocation.

    • Assigning to ([ref] $prevPath).Value instead is a convenient, albeit somewhat obscure way to refer to the $prevPath in the parent scope, i.e. the caller's.
      It is the equivalent of using the more verbose (Get-Variable -Scope 1 prevPath).Value
  • Enclosing the assignment in (...) passes the assigned value through, i.e. also outputs the value.

  • Note: Use of Format-Table would be preferable in this case, given that the output modification is only for display purposes.
    However, up to at least PowerShell 7.3.4 a long-standing bug prevents that - see GitHub issue #19509