1

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 ================

AnR
  • 1,809
  • 3
  • 26
  • 45
  • 1
    Add before each line : Write-host "Comparing file1.js" – jdweng Apr 14 '23 at 12:26
  • You are running the command compare-object three different times. It is not a separate line for each command. – jdweng Apr 14 '23 at 14:13
  • @mklement0: another column can easily be added, but that is not how current code is written. If OP wants an array of different files then create the array. – jdweng Apr 14 '23 at 14:38
  • @jdweng I think Write-host or even Echo is a good idea to start with. I must admit that I need to learn a lot in this area. Thank you all for your help. I now need to convert this into a Loop as there are dozens of recursive folders and thousands of files in there to compare. Initially I need to only know which files have differences. – AnR Apr 14 '23 at 19:01
  • @mklement0 as mentioned in my question "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)." I only want to know in my output file which pair of files the difference belongs to (I mention anyone) as there will be thousands of files to compare – AnR Apr 14 '23 at 19:34

2 Answers2

2

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

mklement0
  • 382,024
  • 64
  • 607
  • 775
0

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.

AnR
  • 1,809
  • 3
  • 26
  • 45