Search code examples
powershellget-childitemcopy-item

Copy the entire folder structure but filter subfolders based on the folder name by two different strings?


How can one copy the entire folder structure but only select some (sub-)folders based on the folder name by two different strings?

I have tried

PS C:\Users> $includes = "*Touch*", "*mpr*"

PS C:\Users Get-ChildItem "C:\Users\Desktop\mro" -Directory |
>>     Where-Object{$_.Name -in $includes} |
>>     Copy-Item -Destination "C:\Users\shizzle\Desktop\shi_data6" -Recurse -Force


This keeps the folder structue, but copies the whole folder structure (not selected by folders containing the string "Touch" or "mpr".

Get-ChildItem -Path 'C:\Users\Desktop\mro' | Copy-Item -Destination 'C:\Users\Desktop\shi_data9' -Recurse -Container -Filter "*Touch*"

doesnt work since it copies the folder and the content specifically by "Touch" and omits all other files (not containing "Touch") in the desired folder and two -filters dont seem to work either (let alone combined with boolean (like "Touch" or "mpr"))


Solution

  • You are testing the name to be an exact match in the $includes array using the -in operator, but that array contains wildcard characters, so will not work.

    Instead, use the regex -match operator and set your $includes variable to be a string with partial names separated by the regex OR (|) character:

    $includes = 'Touch|mpr'
    Get-ChildItem "C:\Users\Desktop\shizzle\mro" -Directory |
        Where-Object{$_.Name -match $includes} |
        Copy-Item -Destination "C:\Users\shizzle\Desktop\shi_data6" -Recurse -Force
    

    As per your comment, Wildcard comparison is not the same as Regex comparison.

    • In a Wildcard comparison (used with operators -like and notlike) the asterisk * means zero or any number of ANY character.
    • In Regex, the same character has a different meaning: zero or more of the PREVIOUS character or group of characters (see the table below)
    • Comparing literal text to find a matching string in an array as you have tried with operator -in does not use wildcards or regex; the string is either found literally or not.

    As for the code, I have tested this (of course using different filepaths) and works, provided the folder for the destination of the copy already exists.
    If that is not the case, start the code with

    if (!(Test-Path -Path "C:\Users\shizzle\Desktop\shi_data6" -PathType Container)) {
        $null = New-Item -Path "C:\Users\shizzle\Desktop\shi_data6" -ItemType Directory
    }
    

    to make sure it is created first.

    Here the results of my test

    Source folder:

    D:\TEST
    +---DoNotCopyThisFolder
    |   +---SubFolder_1
    |   |       File_1.txt
    |   |       File_2.txt
    |   |       File_3.txt
    |   |
    |   \---SubFolder_2
    |           File_1.txt
    |           File_2.txt
    |           File_3.txt
    |
    +---mpr_Files
    |   +---SubFolder_1
    |   |       File_1.txt
    |   |       File_2.txt
    |   |       File_3.txt
    |   |
    |   +---SubFolder_2
    |   |       File_1.txt
    |   |       File_2.txt
    |   |       File_3.txt
    |   |
    |   \---SubFolder_3
    |           File_1.txt
    |           File_2.txt
    |           File_3.txt
    |
    +---NotThisOne
    |   +---SubFolder_1
    |   |       File_1.txt
    |   |       File_2.txt
    |   |       File_3.txt
    |   |
    |   +---SubFolder_2
    |   |       File_1.txt
    |   |       File_2.txt
    |   |       File_3.txt
    |   |
    |   \---SubFolder_3
    |           File_1.txt
    |           File_2.txt
    |           File_3.txt
    |
    \---Touch
        +---SubFolder_1
        |       File_1.txt
        |       File_2.txt
        |       File_3.txt
        |
        +---SubFolder_2
        |       File_1.txt
        |       File_2.txt
        |       File_3.txt
        |
        \---SubFolder_3
                File_1.txt
                File_2.txt
                File_3.txt
    

    Destination folder:

    D:\SHI_DATA6
    +---mpr_Files
    |   +---SubFolder_1
    |   |       File_1.txt
    |   |       File_2.txt
    |   |       File_3.txt
    |   |
    |   +---SubFolder_2
    |   |       File_1.txt
    |   |       File_2.txt
    |   |       File_3.txt
    |   |
    |   \---SubFolder_3
    |           File_1.txt
    |           File_2.txt
    |           File_3.txt
    |
    \---Touch
        +---SubFolder_1
        |       File_1.txt
        |       File_2.txt
        |       File_3.txt
        |
        +---SubFolder_2
        |       File_1.txt
        |       File_2.txt
        |       File_3.txt
        |
        \---SubFolder_3
                File_1.txt
                File_2.txt
                File_3.txt
    

    As for Regex, there are a few special characters that have special meaning:

    Special Characters in Regex

    Char Description Meaning
    \ Backslash Used to escape a special character
    ^ Caret Beginning of a string
    $ Dollar sign End of a string
    . Period or dot Matches any single character
    | Vertical bar or pipe symbol Matches previous OR next character/group
    ? Question mark Match zero or one of the previous
    * Asterisk or star Match zero, one or more of the previous
    + Plus sign Match one or more of the previous
    ( ) Opening and closing parenthesis Group characters
    [ ] Opening and closing square bracket Matches a range of characters
    { } Opening and closing curly brace Matches a specified number of occurrences of the previous