Search code examples
powershellexceptget-childitemfile-move

Powershell move folders with exclude


I have a lots of folders with contents that I want to move to another folder Below I want to move the 3 folders to the ARKIV folder - it works but the script crasches at the end. Am I using the Exclude flag the wrong way? If I use -Whatif it seems to want to move that folder as well - see picture 2

My code is as follows:

$current_logfiles = "H:\FAKTURA\"
$destination = "H:\FAKTURA\ARKIV\"
$excludes = "H:\FAKTURA\ARKIV\"

if ((Test-Path -Path $destination)) 
{
    Get-ChildItem -Exclude $excludes  -Path H:\FAKTURA  | Move-Item  -Destination 
    H:\FAKTURA\ARKIV -Whatif
}

enter image description here

enter image description here


Solution

  • Am I using the Exclude flag the wrong way?

    Yes:

    • -Exclude (like -Include) only operates on item (file or directory) names, not entire paths.

    • -Excludes only excludes the matching item itself. This means that when -Recurse is used and a directory is excluded, its contents (subtree) are not.

    In your case, since you're not using -Recurse and are therefore only targeting the immediate child items of your input directory, the solution is to use the name only, i.e. $excludes = 'ARKIV':

    $excludes = 'ARKIV'
    Get-ChildItem -Exclude $excludes -Path H:\FAKTURA  | 
      Move-Item  -Destination H:\FAKTURA\ARKIV -Whatif
    

    Note: Due to a bug in Windows PowerShell, you must use -Path in the command above, even though -LiteralPath is conceptually appropriate and in general more robust.

    (The code could be simplified: you could use $destination with -Destination and (Split-Path -Leaf $destination) as the $excludes value.
    To skip items that are already present in $destination, insert the following pipeline segment before Move-Item:
    Where-Object { -not (Test-Path -LiteralPath (Join-Path $destination $_.Name)) })


    Recursive use cases currently not supported by -Exclude:

    It follows from the above that, when -Recurse is used:

    • You cannot exclude items by subtree path.

    • You cannot exclude items along with their subtrees

    These limitations apply to Windows PowerShell and to PowerShell (Core) up to at least the version current as of this writing, v7.3.x.
    The next section links to feature requests that may provide this functionality in the future.

    Workarounds, based on post-filtering via Where-Object:

    In the simplest case, if the paths to exclude can be matched via wildcard expressions, use -notlike; e.g.:

    Get-ChildItem -Recurse -File -Filter *.txt |
      Where-Object FullPath -notlike Arkiv/* |
      Where-Object FullPath -notlike */sub/dir/* |
    

    This works for excluding the content of the directories of interest, but not these directories themselves (which isn't a problem in this example, given that -File is used to enumerate only files).

    While you could add another -notlike operation that omits the /* in order to also match the directories themselves (e.g., -notlike */sub/dir), such duplication is obviously cumbersome.
    An alternative is to use regexes with -notmatch instead:

    Get-ChildItem -Recurse -Directory |
      Where-Object FullPath -notmatch '(^|\\)node_modules(\\|$)'
    
    • This excludes directories named node_modules and their subtrees wherever they may be located in the input directory subtree.

    • Note that \\ matches a verbatim \, i.e. a Windows path separator; use / on Unix-like platforms or [\\/] for cross-platform compatibility.

    • This answer shows a programmatic way to construct these regexes, based on an array of verbatim exclusion paths.


    Potential future enhancements:

    There are two relevant feature requests to potentially overcome the limitations of -Exclude in the future: