0

I am new to powershell and using it as a one off to help me extract some files I've collected during my experiments.

I have many data sets with nested subfolders of processed data. They all have a folder in the path called "VPP" and I want to extract only the files in this folder and copy them to a new location. I want to do this while preserving the existing directory structure, so the parent folders will all be empty but the directory structure will be preserved. I need it like this so that I can preserve the organisation of the data (since I identify datasets from the parent folder name at the top of the tree) and so that I can copy files back to the original folder at a later date and have them end up in the right place.

So far, I have adapted a script I found online:

$sourceDirectory = "F:\OneDrive - University of Edinburgh\PhD\Results\Campaign1_Part4\GustData"
$destDirectory = "C:\Users\Anjali\Desktop\test"    
$keyword = "VPP"
$children = Get-ChildItem -Path $sourceDirectory -Recurse

foreach ($child in $children)
{
    if ($child -match $keyword)
    {
        try
        {                               
            Copy-Item -Path "$($sourceDirectory)\$($child )" -Destination $destDirectory -Force -Recurse
        }
        catch [System.Exception] 
        {
            Write-Output $_.Exception
        }
    }
}

The problem I have is that the $child variables are only the filename and do not have any of the intermediate subfolders. I want $child to be the file path starting from $sourceDirectory e.g. "dataset1\folderA\folderB\VPP\file1" but it's coming out "file1".

How do I retrieve the intermediate filepath?

6
  • 2
    $child.FullName ? Commented Mar 23, 2024 at 13:46
  • 1
    Use a string operation like $child.FullName.Substring($sourceDirectory.Length) or the Resolve-Path command. Commented Mar 23, 2024 at 15:50
  • 1
    Why would you want just that part of the file path? Your code is then combining it with $sourceDirectory again to get the full path so you can copy it.. As aside, Get-ChildItem also returns DirectoryInfo objects, not just FileInfo objects unless you tell it what you want returned using parameter -File or -Directory Commented Mar 23, 2024 at 15:54
  • 1
    As commented, use Copy-Item -Path $child.FullName instead of trying to manually combine the source path with the stringified object $child. P.S. With this: if ($child -match $keyword) do you want to match the file Name with $keyword or should that be a match on the entire full path and filename? Since you're not specifying what property of $child to use, you are testing the stringified form Powershell uses on FileInfo objects, which happens to be the .Name property Commented Mar 23, 2024 at 16:05
  • 1
    @Theo I need just part of the path so I can combine with the destination base path. Thanks for spotting the way I was using $child, I made a mistake in the for loop and really confused myself. I would like the keyword to match folder names too i.e. for a folder called "VPP" copy all the files inside to the corresponding folder in the destination folder tree. The code inside the for loop is now Copy-Item -Path $child.fullName -Destination "$($destDirectory)\$($child.fullName.substring($sourceDirectory.length))" -Force -Recurse Commented Mar 23, 2024 at 18:28

2 Answers 2

1

This will copy the VPP directories to a new location while maintaining the directory structure.

When you are confident that the correct directories will be copied, remove the -WhatIf switch from the Copy-Item command.

Note, this code will remove all files under the destination directory before copying. This is to ensure that no stray. extraneous files are left lying around. You may want to change that.

You will need to change the first line specifying the source directory.

$sourceDirectory = 'C:\src\t\so\78211086\'
$destDirectory = Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop' -AdditionalChildPath 'test'
$keyword = 'VPP'

# Set the current directory to the source directory for Resolve-Path.
Push-Location $sourceDirectory

# Remove any existing data set directory and create it anew.
if (Test-Path -Path $destDirectory) { Remove-Item -Recurse -Force $destDirectory }
New-Item -ItemType Directory -Path $destDirectory | Out-Null

Get-ChildItem -Recurse -Directory -Filter $keyword | ForEach-Object {
    $VppPath = Resolve-Path -Path $_.FullName -Relative
    $DestinationPath = Join-Path -Path $destDirectory -ChildPath $VppPath
    Copy-Item -Recurse -Path $VppPath -Destination $DestinationPath -WhatIf
}

# Return to whatever the directory was before this started.
Pop-Location

Show the contents of the destination directory with the following command.

Get-ChildItem -Recurse $destDirectory | Select-Object -Property FullName
Sign up to request clarification or add additional context in comments.

1 Comment

Wow! Thank you so much! I managed to get something working yesterday but your program looks much cleverer and probably more robust than mine. I will give it a go tomorrow!
0

You could also do this without changing the current location like below:

$sourceDirectory = 'F:\OneDrive - University of Edinburgh\PhD\Results\Campaign1_Part4\GustData'
$destDirectory   = 'C:\Users\Anjali\Desktop\test'
$keyword         = 'VPP'

Get-ChildItem -Path $sourceDirectory -Directory -Recurse -Filter $keyword | ForEach-Object {
    # construct the destination folder path
    $targetPath = Join-Path -Path $destDirectory -ChildPath $_.FullName.Substring($sourceDirectory.Length)
    # create the target destination folder if this does not already exist
    $null = New-Item -Path $targetPath -ItemType Directory -Force
    $_ | Copy-Item -Destination $targetPath -Recurse -Force
}

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.