how to copy-items to subfolder

Hi All

Novice here, so please excuse my simple question, however, in the way the code below is scripted to copy from one location to the other, it’s not so simple selecting the nested folder.

The code works completey fine, which is, it grabs the contents of the source directories and copies the files to the destination location as long as the same name as the source folder exists. My problem is, I want to have an Archive Folder within the destination and copy to this. I can’t seem to work out how to do this.

Also, please ignore the Write-Progress code, I have been trying to get this working, but can’t get an accurate display (percentage complete) as there isn’t an actual foreach loop doing the copying… ?

Any help is much appreciated.

Regards
Chris

# First, get a list of all target directories (i.e.: directories under $destination that have a match in $source)
$source = "c:\folder script\copy"
$destination = "c:\folder script\test"
$countFiles = dir -Path $source -Recurse

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | out-null;

    $targetDirs = dir -Path $source -Recurse -Force |
        ?{ $_.psIsContainer } |
        %{ $_.FullName -replace [regex]::Escape($source), $destination } |
        %{ if (Test-Path $_) { $_ }}

$counter=1        

    # Then, enumerate all files from source and copy them only if the corresponding target dir exists



    dir -Path $source -Recurse -Force | 
        ?{ -not $_.psIsContainer } |                
        ?{ Test-Path (Split-Path ($_.FullName -replace [regex]::Escape($source), $destination) -Parent)} |
        copy -Force -Destination { $_.FullName -replace [regex]::Escape($source), $destination 
        
        $status = "Copying total of {1} Files" -f $counter,$countFiles.Count, $countFiles
        Write-Progress -Activity "Copying data to DLAB" $status -PercentComplete ($counter / $countFiles.Count*100)
        
        }
}

Why not use Copy-Item cmdlet?

I’m unsure what you’re trying to do and how it is failing. The Copy-Item cmdlet has a -Recurse switch which will recursively copy a directory structure.

Hi Guys

Sorry if I didn’t explain this properly.

the code currently looks at a set of source folders, then checks to make sure in the target directory, there is a subfolder with the same name as all the source folders. Then it will copy ONLY files to the destination only if the destination and source folder name match.

Thing I want to do though is copy the files to a subfolder within the destination folder structure called ‘Archive’.

So, we have let’s say:

\source\Folder1
\source\Folder2
\source\Folder3

copy Files from Folder1, 2, 3 to Folder???\Archive when Folder1 and Folder??? etc match.

\destination\Folder???\Archive
\destination\Folder???\Archive
\destination\Folder???\Archive

Hope that makes more sense?

Would something like this work?

$sourceRoot = '\\source'
$destinationRoot = '\\destination'

foreach( $dir in (Get-ChildItem -Path $sourceRoot -Directory) )
{
    # Creates \\destination\Folder1 path
    $destination = Join-Path $destinationRoot $dir.Name
    # Creates \\destination\Folder1\Archive path
    $destination = Join-Path $destination 'Archive'
    
    # Recursively copies \\source\Folder1 to \\destination\Folder1\Archive
    Copy-Item -Source $dir.FullName -Destination $destination -Recurse
}

Hi Aaron

Almost… this is what’s happening with this code

What if: Performing operation “Copy Directory” on Target “Item: C:\folder script\copy\aabd1612 Destination: C:\folder script\test\aab
d1612\Archive\aabd1612”.

What if: Performing operation “Copy Directory” on Target “Item: C:\folder script\copy\aabd1906 Destination: C:\folder script\test\aab
d1906\Archive\aabd1906”.

What if: Performing operation “Copy Directory” on Target “Item: C:\folder script\copy\aack2410 Destination: C:\folder script\test\aac
k2410\Archive\aack2410”.

What if: Performing operation “Copy Directory” on Target “Item: C:\folder script\copy\aada2007 Destination: C:\folder script\test\aad
a2007\Archive\aada2007”.

What if: Performing operation “Copy Directory” on Target “Item: C:\folder script\copy\test Destination: C:\folder script\test\test\Ar
chive”.

$sourceRoot = 'c:\folder script\copy'
$destinationRoot = 'c:\folder script\test'
 
foreach( $dir in (Get-ChildItem -Path $sourceRoot) )
{
    # Creates \\destination\Folder1 path
    $destination = join-path $destinationRoot $dir.Name
    # Creates \\destination\Folder1\Archive path
    $destination = Join-Path $destination "Archive"
 
    # Recursively copies \\source\Folder1 to \\destination\Folder1\Archive
    Copy $dir.FullName -Destination $destination
}

so, no files actually copied, just subdirectory created inside Archive folder.

Oops. I forgot to add the -Recurse switch to Copy-Item.

$sourceRoot = 'c:\folder script\copy'
$destinationRoot = 'c:\folder script\test'
 
foreach( $dir in (Get-ChildItem -Path $sourceRoot) )
{
    # Creates \\destination\Folder1 path
    $destination = join-path $destinationRoot $dir.Name
    # Creates \\destination\Folder1\Archive path
    $destination = Join-Path $destination "Archive"
 
    # Recursively copies \\source\Folder1 to \\destination\Folder1\Archive
    Copy $dir.FullName -Destination $destination -Recurse
}

no - you had it originally Aaron, but I was trying something out, but still didn’t work. By adding it back yes, it does copy files, but the issue still remains as I posted earlier… The output is looking like this… Check the WhatIf statement…

Destination: C:\folder script\test\aabd1612\Archive(b)aabd1612(/b)" it’s adding the $dir.Name inside the archive folder which is not what I am trying to do.

the Archive should be the lowest level.

I’ve tried all sorts of things, but haven’t worked out how yet.

regards.

If the Archive directory exists when you run your script,Copy-Item assumes you want the source directory inside the destination directory. Look at your last WhatIf message:

[blockquote]
What if: Performing operation “Copy Directory” on Target “Item: C:\folder script\copy\test Destination: C:\folder script\test\test\Archive”.
[/blockquote]

It looks like that one was going to copy correctly, probaby because C:\folder script\test\test\Archive didn’t exist.

The only way to work around this is to check if the destination directory exists, and if it does, loop through all the contents of the source directory, and copy those to the destination:

$sourceRoot = 'c:\folder script\copy'
$destinationRoot = 'c:\folder script\test'
 
foreach( $dir in (Get-ChildItem -Path $sourceRoot) )
{
    # Creates \\destination\Folder1 path
    $destination = join-path $destinationRoot $dir.Name
    # Creates \\destination\Folder1\Archive path
    $destination = Join-Path $destination "Archive"
 
    # Recursively copies \\source\Folder1 to \\destination\Folder1\Archive
    if( (Test-Path -path $destination -PathType Container) )
    {
        foreach( $subItem in (Get-ChildItem -Path $dir) )
        {
            Copy-Item -Path $subItem -Destination $destination -Recurse
        }
    }
    else
    {
        Copy $dir.FullName -Destination $destination -Recurse
    }
}

Aaron your latest test-Path is returning the following error.

Test-Path : Cannot bind parameter because parameter ‘Path’ is specified more than once. To provide multiple values to parameters tha
t can accept multiple values, use the array syntax. For example, “-parameter value1,value2,value3”.
At C:\folder script\test_copy.ps1:12 char:44

  • if( (Test-Path -path $destination -path <<<<  Container) )
    

Change

Test-Path -path $destination -path Container

Should be

Test-Path -path $destination -pathtype Container

seems we can’t test the $destination variable for container object as it’s a string?

$destination.PsObject

Members : {char Chars(int index) {get;}, System.Int32 Length {get;}, char get_Chars(int index), bool Equals(System.Object obj), bool Equals (string value), bool Equals(string value, System.StringComparison comparisonType)…}
Properties : {System.Int32 Length {get;}}
Methods : {char get_Chars(int index), bool Equals(System.Object obj), bool Equals(string value), bool Equals(string value, System.StringComparison comparisonType), System.Void CopyTo(int sourceIndex, char destination, int destinationIndex, int count), char ToCharArray(), char ToCharArray(int startIndex, int length)…}
ImmediateBaseObject : c:\folder script\test\aada2007\Archive
BaseObject : c:\folder script\test\aada2007\Archive
TypeNames : {System.String, System.Object}

What error are you getting? Why do you say you can’t test $destination because it’s a string? It should be a string.

ok it accepted the -pathtype… now I get this.

Get-ChildItem : Cannot find path ‘C:\aada2007’ because it does not exist.
At C:\folder script\test_copy.ps1:15 char:44

  •     foreach( $subItem in (Get-ChildItem <<<<  -Path $dir) ).
    

Get-ChildItem is converting $dir from a DirectoryInfo to a string. It does that by calling $dir.ToString(), which returns just its name. Oops. Change it to

Get-ChildItem -Path $dir.FullName

great we got there. I also had to include the .FullName property on $subItem as without it was just selecting files to copy from C:\

{
        foreach( $subItem in (Get-ChildItem -Path $dir.FullName ) )
        {
            Copy-Item $subItem.Fullname -Destination $destination -Recurse
        }
    }

Thanks very much for your help mate. It’s a learning curve for me.

I will now have a go at applying a percentage complete calculation… see how I go…