Search code examples
powershelldirectorysubdirectorycopy-item

powershell - Copy-Item creates empty folder


I wanted to copy a bunch of jpg/gif files with starting Name into another location. It works not as intended. It creates addition folders which stays emtpy and do not copy all subfolders. Then I run again through the whole path and remove all empty folders.

How can I prevent Copy-Item from creating addition empty folders? I want to keep the current path with a new root and add a new subdirectory with the starting letters of the filename and put all the files there.

current folder structure

F:\pics\2016\071310
|
-----> K001
  | 
  ------> 0494434-002394
  ------> 0494434-002394
.
.
------> K0073

wanted folder structure

C:\test\2016\071310
|
-----> K001
  | 
  ------> 0494434-002394
     |
     ----> K-G
     ----> K-F
     .
     .
  ------> 0494434-002394
     |
     ----> K-G
     ----> K-F
     .
     .
.
.
------> K0073

Here is my code

$source = "F:\pics\2016\071310"
$destination = "C:\test" 
$folder = "K-G\"
$filterKG = [regex] "^K-G.*\.(jpg|gif)"

$bin = Get-ChildItem -Exclude $folder -Path $source -Recurse | Where-Object {($_.Name -match $filterKG) -or ($_.PSIsContainer)}

foreach ($item in $bin){
$new_folder = $item.FullName.ToString().Replace($source,$destination).Replace($item.Name,$folder)

if(-not (Test-Path $new_folder)){
    New-Item -Type dir $new_folder
    Write-Host $new_folder
}
Copy-Item $item.FullName -Destination $item.FullName.ToString().Replace($source,$destination).Replace($item.Name,$folder+$item.Name) 
}

#remove empty folders which where added 
$tdc="C:\test"
do {
  $dirs = gci $tdc -directory -recurse | Where { (gci $_.fullName -Force).count -eq 0 } | select -expandproperty FullName
  $dirs | Foreach-Object { Remove-Item $_ }
} while ($dirs.count -gt 0)

Solution

  • Current Script

    • $_.PSIsContainer - I think you're getting these to keep the folder structure but best to ignore as you're not directly interested in them. AFAICS this is why you're getting empty folders.
    • -Exclude $folder - This isn't doing anything; folder with name K-G will still be included.

    • $item.FullName.ToString() - The ToString() is doing nothing as $item.FullName is already a string. Run $item.Fullname.GetType().Name to see.


    Proposed Script

    • Split-path allows you to get the directory a file is in using -Parent.

    • .Replace($source,$destination) is as in your original script.

    • You don't need to hardcode $folder to get it in your destination. Just use $item.Name.SubString(0,3) to get the first 3 letters of a filename.
      string manipulation demo


    $bin = Get-ChildItem -Path $source -Recurse | Where-Object {($_.Name -match $filterKG)}
    
    foreach ($item in $bin){
    
        $new_folder = (Split-path $item.Fullname -Parent).Replace($source,$destination) + "\" + $item.Name.SubString(0,3) + "\"
    
        if(-not (Test-Path $new_folder)){
            New-Item -Type dir $new_folder
            Write-Host $new_folder
        }
    
        Copy-Item $item.FullName -Destination $new_folder 
    
    }