12

I wrote a powershell script to compare the content of two folders:

$Dir1 ="d:\TEMP\Dir1"
$Dir2 ="d:\TEMP\Dir2"

function Test-Diff($Dir1, $Dir2) {
    $fileList1 = Get-ChildItem $Dir1 -Recurse | Where-Object {!$_.PsIsContainer} | Get-Item | Sort-Object -Property Name
    $fileList2 = Get-ChildItem $Dir2 -Recurse | Where-Object {!$_.PsIsContainer} | Get-Item | Sort-Object -Property Name

    if($fileList1.Count -ne $fileList2.Count) {
        Write-Host "Following files are different:"
        Compare-Object -ReferenceObject $fileList1 -DifferenceObject $fileList2 -Property Name -PassThru | Format-Table FullName
        return $false
    }

    return $true
}

$i = Test-Diff $Dir1 $Dir2

if($i) { 
    Write-Output "Test OK" 
} else { 
    Write-Host "Test FAILED" -BackgroundColor Red
}

If I set a break point on Compare-Object, and I run this command in console, I get the list of differences. If I run the whole script, I don't get any output. Why?

I'm working in PowerGUI Script Editor, but I tried the normal ps console too.

EDIT:

The problem is the check on the end of the script.

$i = Test-Diff $Dir1 $Dir2

if($i) { 
   Write-Output "Test OK" 
...

If I call Test-Diff without $i = check, it works!

Test-Diff returns with an array of objects and not with an expected bool value:

[DBG]: PS D:\>> $i | ForEach-Object { $_.GetType() } | Format-Table -Property Name
 Name                                                                                                                        
 ----                                                                                                                        
 FormatStartData                                                                                                             
 GroupStartData                                                                                                              
 FormatEntryData                                                                                                             
 GroupEndData                                                                                                                
 FormatEndData                                                                                                               
 Boolean         

If I comment out the line with Compare-Object, the return value is a boolean value, as expected.

The question is: why?

5
  • 4
    Please show the command in the context of your script, since this command itself runs successfully.
    – Kohlbrr
    Mar 26, 2014 at 14:26
  • I extracted the problematic part of the script, it works from script also. I'm completely confused...
    – Mattia72
    Mar 26, 2014 at 15:06
  • The if is the problem on the end. if($i) { Write-Output "Test OK" ...
    – Mattia72
    Mar 26, 2014 at 15:11
  • Could you explain what the purpose of the script is? And does the test at the end fail for you?
    – Kohlbrr
    Mar 26, 2014 at 15:23
  • Purpose: comparing directories. No, the test fails never, if I check the return value.
    – Mattia72
    Mar 26, 2014 at 15:25

3 Answers 3

16

I've found the answer here: http://martinzugec.blogspot.hu/2008/08/returning-values-from-fuctions-in.html

Functions like this:

Function bar {
 [System.Collections.ArrayList]$MyVariable = @()
 $MyVariable.Add("a")
 $MyVariable.Add("b")
 Return $MyVariable
}

uses a PowerShell way of returning objects: @(0,1,"a","b") and not @("a","b")

To make this function work as expected, you will need to redirect output to null:

Function bar {
 [System.Collections.ArrayList]$MyVariable = @()
 $MyVariable.Add("a") | Out-Null
 $MyVariable.Add("b") | Out-Null
 Return $MyVariable
}

In our case, the function has to be refactored as suggested by Koliat.

2
  • In my case, I figured if I want to return a failure from a function, a reliable way is to throw an error instead of returning $false.
    – orad
    Jun 12, 2017 at 0:23
  • 2
    Who is Koliat? Found nobody with that name on this page.
    – ffonz
    Mar 21, 2021 at 12:33
4

An alternative to adding Out-Null after every command but the last is doing this:

$i = (Test-Diff $Dir1 $Dir2 | select -last 1)

PowerShell functions always return the result of all the commands executed in the function as an Object[] (unless you pipe the command to Out-Null or store the result in a variable), but the expression following the return statement is always the last one, and can be extracted with select -last 1.

1
  • The readability of having variables assigned and not using makes this approach problematic. | Out-Null seems to show intent much better.
    – FabianVal
    Jan 26 at 21:18
1

I have modified the bit of your script, to make it run the way you want it. I'm not exactly sure you would want to compare files only by the .Count property though, but its not within the scope of this question. If that wasn't what you were looking after, please comment and I'll try to edit this answer. Basically from what I understand you wanted to run a condition check after the function, while it can be easily implemented inside the function.

$Dir1 ="C:\Dir1"
$Dir2 ="C:\Users\a.pawlak\Desktop\Dir2"

function Test-Diff($Dir1,$Dir2)
{
$fileList1 = Get-ChildItem $Dir1 -Recurse | Where-Object {!$_.PsIsContainer} | Get-Item | Sort-Object -Property Name
$fileList2 = Get-ChildItem $Dir2 -Recurse | Where-Object {!$_.PsIsContainer} | Get-Item | Sort-Object -Property Name

if ($fileList1.Count -ne $fileList2.Count)
{
Write-Host "Following files are different:"
Compare-Object -ReferenceObject $fileList1 -DifferenceObject $fileList2 -Property FullName -PassThru | Format-Table FullName
Write-Host "Test FAILED" -BackgroundColor Red

}
else 
{ 
return $true
Write-Output "Test OK" 
}
}

Test-Diff $Dir1 $Dir2

If there is anything unclear, let me know

AlexP

5
  • Yes, it works, but why returns the original function an array rather then a bool value?
    – Mattia72
    Mar 26, 2014 at 15:39
  • Test-Diff function consists of couple of commands rather than a simple Bool value. Test-Diff function is an array of 6 commands, where the last one is the bool type. Change the original code to if($i[5]) and see the results. $i | Get-Member and $i.GetType() to see the details. If you call for the last object in this array, you are returning the bool function you were looking for. Thanks for the question though, I did some research and expanded my own understanding :-)
    – AlexPawlak
    Mar 26, 2014 at 15:55
  • 3
    It's not the excepted behavior in my opinion, if I write 'return $false' explicitly. I think $i should be $true or $false and not an array of commands.
    – Mattia72
    Mar 26, 2014 at 17:24
  • As far as I see $i is a function consisting of many types, rather than a simple $i = $true | $false. However, I am not experienced enough to state if something is working as designed or a bug. Is the solution I presented clear nonetheless?
    – AlexPawlak
    Mar 26, 2014 at 17:30
  • Yes, thanks, it's clear. Only the 'return $true' in the else branch is not clear. It may be a copy-paste mistake.
    – Mattia72
    Mar 26, 2014 at 17:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.