locked
Copying files using the ForEach-Object commandlet RRS feed

  • Question

  • I want to create a script that copies files from a source directory on my local machine to a network drive that creates a folder path with the date in the name. I want to use a foreach loop to copy each file as a separate object. I'm sure there are some simpler ways to do this function but I am learning powershell and thought this would make for a good learning exercise for learning the ForEach-Object commandlet.

    Here is what I have and the error I am getting, need help to figure out why? Thank You...

    ---------------- Script Starts Here--------------------

    # Backup Script for Buddy's workstation
    $dtoday = get-date -format "yyyyMMdd"
    $sourcepath = "C:\Users\bfox\Desktop\Docs"
    $destination = "\\file1\users$\bfox\backups\$dtoday"
    $sourcefiles = get-childitem $sourcepath  -file -Recurse

    foreach ($file in $sourcefiles)
    {

    Copy-Item $sourcefiles -Destination $destination

    }

    ------------------Script Ends here-------------------------

    Here is the error I get:

    Copy-Item : Cannot find path 'C:\Users\bfox\Desktop\Docs\WTX_8.1 Delpoys.docx' because it does not exist.
    At C:\Users\bfox\Desktop\Docs\scripts\bud_backup.ps1:13 char:2
    +  Copy-Item $sourcefiles -Destination $destination
    +  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (C:\Users\bfox....1 Delpoys.docx:String) [Copy-Item], ItemNotFoundException
        + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.CopyItemCommand


    • Edited by Buddy Fox Wednesday, July 31, 2013 10:28 PM
    Wednesday, July 31, 2013 10:26 PM

Answers

  • Thanks guys! I found a working solution with all your help. I actually figured something out for creating the folder paths first. Using an if statement and the test-path commandlet you can do something like this. This was a great learning project for me. Thanks!

    foreach ($file in $sourcefiles)
        {
        $newdir = $file.DirectoryName.Replace( $sourcepath, $destination )
         
         If (-not (test-path $newdir))
         {
            md $newdir
         }
         
          Copy-Item -Path $file.FullName -Destination $newdir
         

         }

    • Marked as answer by Yan Li_ Monday, August 12, 2013 12:28 PM
    Friday, August 9, 2013 7:00 PM
  • The entire script looks like this now, and it works perfectly with Powershell 3.0. Now the reason I have for creating a foreach loop to begin with was to do some logging on this. I'll be working on that part next. In the end, robocopy is probably easier. But this below script will work!

    ++++++++++++++++++++++++++++

    $dtoday = get-date -format "yyyyMMdd"
    $sourcepath = "C:\Test\TestA"
    $destination = "C:\Test\TestB\$dtoday"
    $sourcefiles = get-childitem $sourcepath -File -Recurse

    foreach ($file in $sourcefiles)
        {
        $newdir = $file.DirectoryName.Replace( $sourcepath, $destination )
         
         If (-not (test-path $newdir))
         {
            md $newdir
         }
         
          Copy-Item -Path $file.FullName -Destination $newdir
          

         }

    +++++++++++++++++++++++++++++++

    • Marked as answer by Yan Li_ Monday, August 12, 2013 12:28 PM
    Friday, August 9, 2013 7:04 PM

All replies

  • When you use foreach, in each iteration, it assigns the first variable the value of an element in the array. Try something like this:

    # Backup Script for Buddy's workstation
    $dtoday = get-date -format "yyyyMMdd"
    $sourcepath = "C:\Users\bfox\Desktop\Docs"
    $destination = "\\file1\users$\bfox\backups\$dtoday"
    $sourcefiles = get-childitem $sourcepath  -file -Recurse
    
    foreach ($file in $sourcefiles)
    {
    
    Copy-Item $file.FullName -Destination "$destination\$($file.Name)"
    
    }

    In this case, you use $file to access each item as it loops through the $sourcefiles collection. $file.FullName references the full path for each item. I've also added the name of the file to the destination.

    Mike

    • Proposed as answer by Mike Laughlin Thursday, August 1, 2013 12:14 AM
    • Marked as answer by Yan Li_ Thursday, August 8, 2013 2:02 AM
    • Unmarked as answer by Yan Li_ Thursday, August 8, 2013 2:02 AM
    Wednesday, July 31, 2013 10:43 PM
  • Thanks Mike! I really appreciate the simple explanation.

    Thursday, August 1, 2013 12:06 AM
  • Just to add to Mike's example, here's a link to my favorite command reference site:

    http://ss64.com/ps/foreach.html

    There's a few examples there to look over as well.


    Don't retire TechNet!

    Thursday, August 1, 2013 12:14 AM
  • OK - I'm still having a bit of trouble getting this to work. I"ve made some local test folders to simplify my source and destinations. Basically I just want to take all the subfolders and files of my source path and copy them to a dated folder on my destination retaining sub folders etc.

    ++++++++++++++++++

    $dtoday = get-date -format "yyyyMMdd"
    $sourcepath = "C:\Test\TestA"
    $destination = "C:\Test\TestB\$dtoday"
    $sourcefiles = get-childitem $sourcepath  -file -Recurse

    foreach ($file in $sourcefiles)
    {
    Copy-Item $file.FullName -Destination "$destination\$($file.Name)"
    }

    +++++++++++++++++++

    I am getting this error:

    Copy-Item : Could not find a part of the path 'C:\Test\TestB\20130801\archive_060112.pst'.
    At C:\Users\buddy\Desktop\Docs\scripts\buddy_backup.ps1:10 char:1
    + Copy-Item $file.FullName -Destination "$destination\$($file.Name)"
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : NotSpecified: (:) [Copy-Item], DirectoryNotFoundException
        + FullyQualifiedErrorId : System.IO.DirectoryNotFoundException,Microsoft.PowerShell.Commands.CopyItemCommand


    • Edited by Buddy Fox Thursday, August 1, 2013 9:35 PM
    Thursday, August 1, 2013 9:34 PM
  • Try this - in your destination, you have a partial path, but I put a directory separator in there. We'll just remove that:

    $dtoday = get-date -format "yyyyMMdd"
    $sourcepath = "C:\Test\TestA"
    $destination = "C:\Test\TestB\$dtoday"
    $sourcefiles = get-childitem $sourcepath  -file -Recurse
    
    foreach ($file in $sourcefiles)
    {
    Copy-Item $file.FullName -Destination "$($destination)$($file.Name)"
    }

    Mike

    Thursday, August 1, 2013 10:14 PM
  • Hi Mike,

    I just tried that again and I get no error message. However I don't get the folder structure on the destination path. I get each file renamed with the "yyyyMMdd" date on the front of it. So an example would be file "abc.txt". is now "20130807". What else do you think is going on? Do we need to utilize some .net in with the power-shell to split the file path? I am trying to do this without using .net.

    Thanks a lot for the feed back its much appreciated!

    Wednesday, August 7, 2013 9:04 PM
  • Buddy,

    Try this.  It will replace the source root folder with the destination root folder, leaving the rest of the path intact.

    $dtoday = get-date -format "yyyyMMdd"
    $sourcepath = "C:\Test\TestA"
    $destination = "C:\Test\TestB\$dtoday"
    $sourcefiles = get-childitem $sourcepath  -file -Recurse
    
    foreach ($file in $sourcefiles)
    {
    Copy-Item $file.FullName -Destination $file.FullName.Replace( $sourcepath, $destination )
    }

    Wednesday, August 7, 2013 10:13 PM
  • OK, I believe were getting somewhere now. I do get the below error message when running the above script.

    The interesting thing is if I manually create the 20130808 folder along with any subfolders those files will copy the right way. So I am wondering if we need to have the script copy just the empty folder structure first?

    ++++++++++++++++++++++++++++++

    Copy-Item : Could not find a part of the path 'C:\Test\TestB\20130808\Deployment Traning Video\lib\thumb.1373579332499.png'.
    At C:\Users\bfox\Desktop\Untitled2.ps1:8 char:9
    +         Copy-Item $file.FullName -Destination $file.FullName.Replace( $sourcepat ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : NotSpecified: (:) [Copy-Item], DirectoryNotFoundException
        + FullyQualifiedErrorId : System.IO.DirectoryNotFoundException,Microsoft.PowerShell.Commands.CopyItemCommand

    +++++++++++++++++++++++++++++++

    Thursday, August 8, 2013 5:03 PM
  • Buddy,

    Ah, yes.  Forgot about that.  That's why I usually just use robocopy.

    $dtoday = get-date -format "yyyyMMdd"
    $sourcepath = "C:\Test\TestA"
    $destination = "C:\Test\TestB\$dtoday"
    $sourcefiles = get-childitem $sourcepath  -file -Recurse
    
    foreach ($file in $sourcefiles)
    {
    Robocopy.exe $file.FullName $file.FullName.Replace( $sourcepath, $destination )
    }

    Thursday, August 8, 2013 7:22 PM
  • Or perhaps...

    $dtoday = get-date -format "yyyyMMdd"
    $sourcepath = "C:\Test\TestA"
    $destination = "C:\Test\TestB\$dtoday"
    $sourcefiles = get-childitem $sourcepath  -file -Recurse
    
    foreach ($file in $sourcefiles)
    {
    Robocopy.exe $file.DirectoryName $file.DirectoryName.Replace( $sourcepath, $destination ) $File.Name
    }

    Sometimes I test these things before I post them.
    Thursday, August 8, 2013 7:26 PM
  • Sometimes I test these things before I post them.
    Bah, where's the fun in that? =]

    Don't retire TechNet!


    Thursday, August 8, 2013 7:44 PM
  • Thanks guys! I found a working solution with all your help. I actually figured something out for creating the folder paths first. Using an if statement and the test-path commandlet you can do something like this. This was a great learning project for me. Thanks!

    foreach ($file in $sourcefiles)
        {
        $newdir = $file.DirectoryName.Replace( $sourcepath, $destination )
         
         If (-not (test-path $newdir))
         {
            md $newdir
         }
         
          Copy-Item -Path $file.FullName -Destination $newdir
         

         }

    • Marked as answer by Yan Li_ Monday, August 12, 2013 12:28 PM
    Friday, August 9, 2013 7:00 PM
  • The entire script looks like this now, and it works perfectly with Powershell 3.0. Now the reason I have for creating a foreach loop to begin with was to do some logging on this. I'll be working on that part next. In the end, robocopy is probably easier. But this below script will work!

    ++++++++++++++++++++++++++++

    $dtoday = get-date -format "yyyyMMdd"
    $sourcepath = "C:\Test\TestA"
    $destination = "C:\Test\TestB\$dtoday"
    $sourcefiles = get-childitem $sourcepath -File -Recurse

    foreach ($file in $sourcefiles)
        {
        $newdir = $file.DirectoryName.Replace( $sourcepath, $destination )
         
         If (-not (test-path $newdir))
         {
            md $newdir
         }
         
          Copy-Item -Path $file.FullName -Destination $newdir
          

         }

    +++++++++++++++++++++++++++++++

    • Marked as answer by Yan Li_ Monday, August 12, 2013 12:28 PM
    Friday, August 9, 2013 7:04 PM
  • Thanks its working perfectly but inside for each is the pblm

    Thursday, November 9, 2017 2:16 PM