PowerShell Genie

Compare two directory inventories

Script 2 min read Windows PowerShell 5.1 and PowerShell 7

Documentation: Microsoft Learn

Both sides get enumerated with Get-ChildItem into a projection of FullName, Length, and LastWriteTime, and Compare-Object is told to compare on exactly those three properties.

The script

1PS> $PathA = "C:\Deploy\Current"
2PS> $PathB = "C:\Deploy\Previous"
3PS> Compare-Object (Get-ChildItem $PathA -File -Recurse | Select-Object FullName,Length,LastWriteTime) (Get-ChildItem $PathB -File -Recurse | Select-Object FullName,Length,LastWriteTime) -Property FullName,Length,LastWriteTime

Script reader

Inspect before you run

Inspection only

Inspect the script below. It does not run in this browser.

Inspection only — no command has run.

    Show reference output
    1 FullName Length LastWriteTime
    2 -------- ------ -------------
    3 C:\Sample\item.txt 18432 9/10/2026 10:00:00 AM
    Inspect this example’s structure

    Read the command and its supplied arguments. This example requires real PowerShell or its module; this diagram does not execute it.

    $PathA = "C:\Deploy\Current" $PathB = "C:\Deploy\Previous" Compare-Object (Get-ChildItem $PathA -File -Recurse | Select-Object FullName,Length,LastWriteTime) (Get-ChildItem $PathB -File -Recurse | Select-Object FullName,Length,LastWriteTime) -Property FullName,Length,LastWriteTime
    1. Stage 1$PathAPositional values or no named parameters

      How it works

      Both sides get enumerated with Get-ChildItem into a projection of FullName, Length, and LastWriteTime, and Compare-Object is told to compare on exactly those three properties. Rows tagged with a left-pointing SideIndicator exist only under $PathA, and right-pointing rows are unique to $PathB.

      Adapting it

      The comparison only means something once the paths are relative, so build a calculated property that trims $PathA and $PathB off the front of FullName and compare on that instead.

      If the result is unexpected

      Every file is reported as a difference, each appearing on exactly one side, even though the two trees were confirmed identical by other means.

      The comparison includes FullName, which embeds the root path, so no file from one tree can ever match a file from the other. Compare-Object is doing precisely what it was told to do. LastWriteTime is also compared at full tick precision, so a copy that preserved timestamps only to the second still registers as different.

      1. Replace FullName with a calculated relative path that strips the root before the comparison runs.
      2. Drop LastWriteTime from the property list, or round it, if the copy method does not preserve sub-second precision.
      3. Compare on Length alone first to confirm the file sets match before adding timestamps back in.
      4. Run Get-FileHash on the remaining differences when content equality is what actually matters.
      5. Check for a trailing separator difference between the two root variables, which shifts every relative path by one character.

      Validate the result

      Comparing the corrected projections across two known-identical trees produces no output at all, which is what Compare-Object emits when the sets match.

      Keep the check reliable

      Normalize to relative paths before any cross-tree comparison, and hash content when the answer has to be authoritative rather than indicative.

      Primary documentation

      Get-ChildItem reference

      Continue
      Genie guideCompare two directory inventories

      Both sides get enumerated with Get-ChildItem into a projection of FullName, Length, and LastWriteTime, and Compare-Object is told to compare on exactly those three properties.