PowerShell Compare-Object Does Not Accept 2 Files as Arguments then Compares Them

343 Views Asked by At

I am writing a PowerShell scripts that turns the following into a script that accepts 2 files as command line arguments, then outputs the difference of the two files to a text file.

Currently, my worthless code is as follows:

# Compare two text files passed as command line arguments

Function compareFiles()
{
    Param (
    [Parameter(mandatory=$true)]
    [string]$file1,
    [string]$file2 
    )
    $file1 = resolve-path \.$file1
    $file2 = resolve-path \.$file2
    if ($file1 -eq $null) {
        Write-Host "No source file."
    }
    if ($file2 -eq $null) {
        Write-Host "No comparison file."
    }

    # Compare the two file objects
    # => means $file2 has that line but not $file1
    # =< means $file1 has that line but not $file2
    # to see lines that arw the same, add -includeequal to end of the following line
    Compare-Object -ReferenceObject (Get-Content $file1) -DifferenceObject (Get-Content $file2) | Out-File "$file1_$file2_Compare.txt" -Force
}

In a PowerShell console, the line Compare-Object -ReferenceObject (Get-Content $obj1) -DifferenceObject (Get-Content $obj2 works fine after defining $obj1="test1.txt" and $obj2="test2.txt".

When I run .\Compare-Files.ps1 -file1 test1.txt -file2 test2.txt from a PowerShell console, I get the following:

compareFiles : Cannot bind argument to parameter 'file1' because it is an empty string.
At C:\Users\debug255\Documents\Fiverr\PowerShell\Orders\armytra1n3d\FO4ADB39A404\Compare-Files.ps1:27 char:21
+ compareFiles -file1 $file1 -file2 $file2 | Out-File "$file1_$file2_Co ...
+                     ~~~~~~
    + CategoryInfo          : InvalidData: (:) [compareFiles], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,compareFiles

This error was resolved by removing the leftover $obj1 and $obj2 variables from earlier testing

This script from Hey Scripting Guy works fine

$fileA = "test1.txt"
$fileB = "test2.txt"

if(Compare-Object -ReferenceObject $(Get-Content $fileA) -DifferenceObject $(Get-Content $fileB))
{
    "files are different"
}
Else
{
    "Files are the same"
}

test1.txt contains the following:

test1
1
2
3
4
5
6
7
asaskjdahs
sl kasldk

test2.txt contains:

test2
1
23
3
4
5
6
799
asaskjdahs
sl kasldkJ

I am using PowerShell 5.1.14393.1066 on Windows 10.

Thank you for your help!

EDIT

I changed the script entirely so that there is no function for the comparison, and I used an example I found on GitHub for opening and comparing MS Word documents, which helped significantly.

# Compare two text files passed as command line arguments

Param (
[Parameter(mandatory=$true)]
    $file1,
    $file2
)

$ErrorActionPreference = 'Stop'

# Remove read-only attributes from baseFile
$baseFile = Get-ChildItem $file1
if ($baseFile.IsReadOnly) {
    $baseFile.IsReadOnly = $false
}

try {
    Compare-Object -ReferenceObject (Get-Content $file1) -DifferenceObject (Get-Content $file2) `
    | Out-File $file1"_"$file2"_Comparison.txt" -Force
}
catch {
    Write-Host "There was an error"
}

However, as soon as I wrap the code in a function, as compareFiles() { ... }, when I run the same line that did work without it being included in a function, .\Compare-Files.ps1 -file1 test1.txt -file2 test2.txt, nothing outputs. When I add a function call in the script below the function compareFiles, it then prompts me for $file1, the fails and does not prompt me for $file2, as it did before, just this time my catch caught it.

Then, I made the Param () global by placing them above the function compareFiles, and it worked!

Now, I need to focus on cleaning up with output a little bit. The output was as follows:

InputObject    SideIndicator
-----------    -------------
    test2      =>           
    1          =>           
    23         =>           
    3          =>           
    4          =>           
    5          =>           
    6          =>           
    799        =>           
    asaskjdahs =>           
    sl kasldkJ =>           
test1          <=           
1              <=           
2              <=           
3              <=           
4              <=           
5              <=           
6              <=           
7              <=           
asaskjdahs     <=           
sl kasldk      <=           

If any of you has any advise to make the output look something like:

test2.txt:

InputObject    SideIndicator
-----------    -------------
    test2      =>           
    1          =>           
    23         =>           
    3          =>           
    4          =>           
    5          =>           
    6          =>           
    799        =>           
    asaskjdahs =>           
    sl kasldkJ =>

    test1.txt

    test1      <=           
    1          <=           
    2          <=           
    3          <=           
    4          <=           
    5          <=           
    6          <=           
    7          <=           
    asaskjdahs <=           
    sl kasldk  <=  

Also, I notice that it says that test1.txt has different content on lines 1, 2, 3, 4, etc, but they are actually the same (1 in test1.txt is the same and on the same line as test2.txt, for example).

Any advise for cleaning this up a bit?

Thank you all for your help!

1

There are 1 best solutions below

2
mklement0 On BEST ANSWER

Note: The question keeps changing and the code exhibits a variety of unrelated problems, so it's hard to give a meaningful answer, but here are a few pointers:

  • Param ( [Parameter(mandatory=$true)] $file1, $file2 ) only defines $file1 as mandatory, not also $file2 - each parameter needs its own [Parameter(...)] attribute.

  • \.$file1 and \.$file2 don't work the way you expect to; perhaps you meant .\$file1 and .\$file2 to refer to files in the current directory, but you don't need any prefix, because Resolve-Path defaults to the current directory anyway.

  • Tests $file1 -eq $null and $file2 -eq $null will never succeed, because $file1 and $file2, due to having been declared [string] parameters, are never $null.

    • A [string]-typed variable in PowerShell defaults to the empty string rather than $null, and even explicitly assigning $null results in the empty string (compare that to C#, where string variables default to null, and assigning null is possible).
  • Expanding (interpolating) variables $file1 and $file2 in string "$file1_$file2_Compare.txt" won't work, because, given that _ is a legal character in a variable name, you must tell PowerShell where the variable names end, by enclosing them in {...}: "${file1}_${file2}_Compare.txt"