Search code examples
powershellrecursionmatchget-childitemcopy-item

Copy folder structure but match subfolders based on the folder name in subdirectories by two different strings?


How can one copy an entire folder structure but only select some folders several subdirectories down based on the folder name by two different strings ('Touch' or 'mpr') ?

the files I need are in e.g.:

"C:\Users\Desktop\shizzle\mro\pre\subject_01\Touch"

but there are two Timepoint-folders (Pre and Post) and several subjects, hence I have tried a higher directory:

$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

which works perfectly, but only if the searched folders are in the specified directory and not several subdirectories down (so it would only work in C:\Users\Desktop\shizzle\mro\pre\subject_01).

Get-ChildItem "C:\Users\shizzle\Downloads\copy_test\copy_test\mro" -Recurse | Where-Object{$_.Name -match $includes} | Copy-Item -Destination "C:\Users\shizzle\Downloads\test_shi10" -Recurse -Force

doesnt not keep up the folder structure.

The folder structure looks like this:

C:\Users\Desktop\shizzle\mro\

+---pre
|   +---subject_01 
|   |   +---dontcopythisfolder
|   |   +---dontcopythisfolder
|   |   +---0024_63_Touch
|   |   |       File_1.txt
|   |   |       File_2.txt
|   |   |       File_3.txt
|   |   +---dontcopythisfolder
|   |   \---654_mpr_8364
|   |          File_1.txt
|   |          File_2.txt
|   |          File_3.txt
|   \---subject_02
|      +---dontcopythisfolder
|      +---dontcopythisfolder
|      +---0024_63_Touch
|      |       File_1.txt
|      |       File_2.txt
|      |       File_3.txt
|      +---dontcopythisfolder
|      \---654_mpr_8364
|             File_1.txt
|             File_2.txt
|             File_3.txt
\---post
   +---subject_01 
   |   +---dontcopythisfolder
   |   +---dontcopythisfolder
   |   +---0024_63_Touch
   |   |       File_1.txt
   |   |       File_2.txt
   |   |       File_3.txt
   |   +---dontcopythisfolder
   |   \---654_mpr_8364
   |          File_1.txt
   |          File_2.txt
   |          File_3.txt
   \---subject_02
      +---dontcopythis folder
      +---dontcopythisfolder
      +---0024_63_Touch
      |       File_1.txt
      |       File_2.txt
      |       File_3.txt
      +---dontcopythisfolder
      \---654_mpr_8364
             File_1.txt
             File_2.txt
             File_3.txt


Solution

  • You can try this:

    $sourcePath  = 'D:\Test'  # 'C:\Users\Desktop\shizzle\mro'
    $Destination = 'D:\Data'  # 'C:\Users\shizzle\Desktop\shi_data6'
    $includes    = 'Touch|mpr'
    Get-ChildItem -Path $sourcePath -Recurse -Directory |
        Where-Object{$_.Name -match $includes} |
        ForEach-Object {
            $targetPath = Join-Path -Path $Destination -ChildPath $_.Parent.FullName.Substring($sourcePath.Length)
            $null = New-Item -Path $targetPath -ItemType Directory -Force
            $_ | Copy-Item -Destination $targetPath -Recurse -Force
        }
    

    Instead of using the Substring() method above, you can also use regex -replace to construct the target path.
    However, keep in mind that a path contains characters that have special meaning for regex, so you should then use the [regex]::Escape()'d version of the source path, and anchor it to the beginning of the string with ^:

    $sourcePath  = 'D:\Test'  # 'C:\Users\Desktop\shizzle\mro'
    $Destination = 'D:\Data'  # 'C:\Users\shizzle\Desktop\shi_data6'
    $includes    = 'Touch|mpr'
    
    # if you want to use regex `-replace` instead, use this:
    $escapedSource = '^{0}' -f [regex]::Escape($sourcePath)
    Get-ChildItem -Path $sourcePath -Recurse -Directory |
        Where-Object{$_.Name -match $includes} |
        ForEach-Object {
            $targetPath = Join-Path -Path $Destination -ChildPath ($_.FullName -replace $escapedSource)
            $null = New-Item -Path $targetPath -ItemType Directory -Force
            $_ | Copy-Item -Destination $targetPath -Recurse -Force
        }
    

    Below the results of my test (using different paths of course..)

    Source

    D:\TEST
    +---post
    |   +---subject_01
    |   |   +---0024_63_Touch
    |   |   |       File1.txt
    |   |   |       File2.txt
    |   |   |
    |   |   +---654_mpr_8364
    |   |   |       File1.txt
    |   |   |       File2.txt
    |   |   |
    |   |   +---dontcopythisfolder
    |   |   \---ignorethisonetoo
    |   \---subject_02
    |       +---0024_63_Touch
    |       |       File1.txt
    |       |       File2.txt
    |       |
    |       +---654_mpr_8364
    |       |       File1.txt
    |       |       File2.txt
    |       |
    |       +---dontcopythisfolder
    |       \---ignorethisonetoo
    \---pre
        +---subject_01
        |   +---0024_63_Touch
        |   |       File1.txt
        |   |       File2.txt
        |   |
        |   +---654_mpr_8364
        |   |       File1.txt
        |   |       File2.txt
        |   |
        |   +---dontcopythisfolder
        |   \---ignorethisonetoo
        \---subject_02
            +---0024_63_Touch
            |       File1.txt
            |       File2.txt
            |
            +---654_mpr_8364
            |       File1.txt
            |       File2.txt
            |
            +---dontcopythisfolder
            \---ignorethisonetoo
    

    Destination

    D:\DATA
    +---post
    |   +---subject_01
    |   |   +---0024_63_Touch
    |   |   |       File1.txt
    |   |   |       File2.txt
    |   |   |
    |   |   \---654_mpr_8364
    |   |           File1.txt
    |   |           File2.txt
    |   |
    |   \---subject_02
    |       +---0024_63_Touch
    |       |       File1.txt
    |       |       File2.txt
    |       |
    |       \---654_mpr_8364
    |               File1.txt
    |               File2.txt
    |
    \---pre
        +---subject_01
        |   +---0024_63_Touch
        |   |       File1.txt
        |   |       File2.txt
        |   |
        |   \---654_mpr_8364
        |           File1.txt
        |           File2.txt
        |
        \---subject_02
            +---0024_63_Touch
            |       File1.txt
            |       File2.txt
            |
            \---654_mpr_8364
                    File1.txt
                    File2.txt