how to copy-items to subfolder

This topic contains 15 replies, has 3 voices, and was last updated by Profile photo of jembsb . jembsb . 2 years, 3 months ago.

  • Author
    Posts
  • #18465
    Profile photo of jembsb .
    jembsb .
    Participant

    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)
            
            }
    }
    
    
  • #18468
    Profile photo of Sam Boutros
    Sam Boutros
    Participant

    Why not use Copy-Item cmdlet?

  • #18496
    Profile photo of Aaron Jensen
    Aaron Jensen
    Participant

    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.

  • #18507
    Profile photo of jembsb .
    jembsb .
    Participant

    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?

  • #18532
    Profile photo of Aaron Jensen
    Aaron Jensen
    Participant

    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
    }
    
    • #18607
      Profile photo of jembsb .
      jembsb .
      Participant

      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.

  • #18637
    Profile photo of Aaron Jensen
    Aaron Jensen
    Participant

    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
    }
    
  • #18646
    Profile photo of jembsb .
    jembsb .
    Participant

    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.

  • #18647
    Profile photo of Aaron Jensen
    Aaron Jensen
    Participant

    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
        }
    }
    
  • #18649
    Profile photo of jembsb .
    jembsb .
    Participant

    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) )

  • #18650
    Profile photo of Aaron Jensen
    Aaron Jensen
    Participant

    Change

    Test-Path -path $destination -path Container
    

    Should be

    Test-Path -path $destination -pathtype Container
    
  • #18651
    Profile photo of jembsb .
    jembsb .
    Participant

    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}

  • #18652
    Profile photo of Aaron Jensen
    Aaron Jensen
    Participant

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

  • #18653
    Profile photo of jembsb .
    jembsb .
    Participant

    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) ).

  • #18654
    Profile photo of Aaron Jensen
    Aaron Jensen
    Participant

    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
    
  • #18655
    Profile photo of jembsb .
    jembsb .
    Participant

    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...

You must be logged in to reply to this topic.