2

I have a script which moves files with a specific extension from a source directory to a destination directory.

My source directory looks like:

FILESFOLDER

  • File1.td

  • File2.td

    SUBFOLDER

    • File3.td

    • File4.td

My script is short and looks like:

if(!(Test-Path $SourceDirPath) -or !(Test-Path $DestinationDirPath))
{
    Write-Host "The source- or destination path is incorrect, please check "
    break
}else
{
    Write-Host "Success"
    Copy-Item -path $SourceDirPath -Filter "*$extension" -Destination $DestinationDirPath -Recurse
}

I have to mention that the $SourceDirPath comes from a config file and only works if I declare it as C:\FILESFOLDER\*

The script works but doesn't copy the files from the subfolder to destination. The destination only has File1 and File2.

What's wrong with my Copy-Item command?

Kahn Kah
  • 1,389
  • 7
  • 24
  • 49

4 Answers4

4

The -Recurse switch has no effect in your case, because it's only applied to the items matched by the combination of $SourceDirPath, which uses a trailing \* to match the items in the source directory, and -Filter, which in your case are only the *.td files located directly in your source directory.

Omitting the trailing \* to target the directory itself (e.g., C:\FOLDER rather than C:\FOLDER\*), solves that problem in principle, but, due to an annoying quirk only works as intended if the destination directory does not exist yet. If it does exist, the items are placed in a subdirectory of the destination directory, named for the source directory.

If there's nothing in the existing destination directory to preserve before copying (and no special attributes of directory itself need preserving, you can work around the problem by deleting the preexisting destination directory beforehand.

if(!(Test-Path $SourceDirPath) -or !(Test-Path $DestinationDirPath))
{
    Write-Host "The source or destination path is incorrect, please check "
    break
}
else
{
    Write-Host "Success"

    # Delete the preexisting destination directory,
    # which is required for the Copy-Item command to work as intended.
    # BE SURE THAT IT IS OK TO DO THIS.
    Remove-Item $DestinationDirPath -Recurse

    # Note: $SourceDirPath must NOT end in \*  
    Copy-Item -Recurse -LiteralPath $SourceDirPath -Filter "*$extension" -Destination $DestinationDirPath
}

If you do need to preserve existing $DestinationDirPath content or attributes (ACLs, ...), more work is needed.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • It didn't work sadly, it also removes my whole destination folder? – Kahn Kah Apr 13 '17 at 13:11
  • @KahnKah: As stated (let me know if I can make that clearer in the answer): **The workaround _requires_ that the destination folder be _removed first_** (I know that's annoying - `Copy-Item` is broken in many ways). Aside from that, however, it should work. So: Are you saying it doesn't work, or are you saying that you must preserve content in the existing destination directory? – mklement0 Apr 13 '17 at 13:13
1

You should use the Get-ChildItem cmdlet with the -recurse parameter to retrieve all files based on your filter condition. Then you can pipe the result to the Copy-Item cmdlet and only have to specify a destination.

Martin Brandl
  • 56,134
  • 13
  • 133
  • 172
  • Would that also let me have the exact same structure both in the source as the destination directory? – Kahn Kah Apr 13 '17 at 12:28
  • You have to take care about that yourself. You may consider using Robocopy for that. – Martin Brandl Apr 13 '17 at 12:30
  • Not quite, this is possible, Do you want to compare before copy or just copy everything regardless of presence at the destination? – Richard Dakin Apr 13 '17 at 12:33
  • I want to copy regardless, @Richard, would you tell me that `Get-ChildItem`wouldn't work in this case? – Kahn Kah Apr 13 '17 at 12:34
  • Martin it doesn't work I tried `Get-ChildItem -Path $SourceDirPath -Filter "*$extension" -Recurse |Copy-Item -Destination $DestinationDirPath` and it just retrieves the files, not the structure with the subfolder – Kahn Kah Apr 13 '17 at 12:38
  • Yes? You mentioned Robocopy but I can't use any external stuff. PS only – Kahn Kah Apr 13 '17 at 12:45
  • 1
    I have told you that you have to take care about the structure yourself. Also, robocopy comes with windows, its not an external tool. – Martin Brandl Apr 13 '17 at 13:05
1

So this will compare the directory source versus the destination and then copy over the content. Ignore my funky variables, I've amended them below on a edit

 #Release folder
    $Source =  ""
    #Local folder
    $Destination = ""

    #Find all the objects in the release
    get-childitem $Source -Recurse | foreach {

    $SrcFile = $_.FullName
    $SrcHash = Get-FileHash -Path $SrcFile -Algorithm MD5 # Obtain hash

    $DestFile = $_.Fullname -replace [RegEx]::escape($Source),$Destination #Escape the hash
    Write-Host "comparing $SrcFile to $DestFile" -ForegroundColor Yellow

    if (Test-Path $DestFile) 
    {
    #Check the hash sum of the file copied
    $DestHash = Get-FileHash -Path $DestFile -Algorithm MD5

    #compare them up
    if ($SrcHash.hash -ne $DestHash.hash) {
        Write-Warning "$SrcFile and $DestFile Files don't match!"

        Write-Warning "Copying $SrcFile to $DestFile " 

        Copy-Item $SrcFile -Destination $DestFile -Force
    } 
    else {
        Write-Host "$SrcFile and $DestFile Files Match" -ForegroundColor 
Green

    }
        } 
    else {
        Write-host "$SrcFile is missing! Copying in" -ForegroundColor Red
        Copy-Item $SrcFile -Destination $DestFile -Force

         }
    }  
Richard Dakin
  • 413
  • 4
  • 16
  • 1
    Doesn't work, you just added the `-Force` and changed the position of the switches? I still only have 2 files in the destination folder – Kahn Kah Apr 13 '17 at 12:48
  • Your updated answer doesn't work. It says `Copy-Item : Cannot bind argument to parameter 'Path' because it is null.`` – Kahn Kah Apr 13 '17 at 12:56
  • 1
    It's because I missed a variable off my copy and paste. I've got to run in a minute but I've added a script above I wrote that does a binary comparison on objects and then recuse copies. This will do what you want, but will also do a binary comparison and report color coded references to the Ps. It's a very long winded way of doing what you want but will do it ! – Richard Dakin Apr 13 '17 at 12:58
  • Thank you very much! It's working but I guess your answer doesn't overwrite if there are already files inside? – Kahn Kah Apr 13 '17 at 13:10
  • 1
    It does, if they are different. Where in your case this would be the only reason you would be updating (if you get my drift) - The MD5 algorithm is great for that as it checks the actually file hash, as well as the contents. It's a comprehensive check rather than some "oh it's the same date" and move on. – Richard Dakin Apr 13 '17 at 13:12
  • 1
    I just realised it indeed does if there are differences between the file. Thanks again for this great piece of code – Kahn Kah Apr 13 '17 at 13:14
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/141647/discussion-between-kahn-kah-and-richard-dakin). – Kahn Kah Apr 13 '17 at 13:21
1

Try this:

$source =  "C:\Folder1"

$destination = "C:\Folder2"

$allfiles = Get-ChildItem -Path $source -Recurse -Filter "*$extension"
foreach ($file in $allfiles){
    if (test-path ($file.FullName.replace($source,$destination))) {
    Write-Output "Seccess"
    Copy-Item -path $file.FullName -Destination ($file.FullName.replace($source,$destination))
    }
    else {
    Write-Output "The source- or destination path is incorrect, please check "
    break
    }
}
Moshe perez
  • 1,626
  • 1
  • 8
  • 17