Copy file based a specified folder based on file name. Create folder if it doesn't exist

214 Views Asked by At

I'm trying to copy files to a specific folder based on a file name.

For example:

Current Folder - C:\Stuff\Old Files\

The File- 206.Little Rock.map.pdf

Destination Folder - D:\Cleanup\206\Repository

So basically the leading number on the file (206) is part of the subfolder. The "\Repository" would stay constant. Only the leading number would change.

If the file was 207.Little Rock.map.pdf then the destination folder would be

D:\Cleanup\207\Repository

I started with a code I got from here but I'm not sure how to account for the change in number and how to make it create a folder if the folder doesn't exist. So 206\Repository would probably already exist, but I would need the script to create the folder if it doesn't.

$SourceFolder = "C:\Stuff\Old Files\"
$targetFolder = "D:\Cleanup\"
$numFiles = (Get-ChildItem -Path $SourceFolder -Filter *.pdf).Count
$i=0

clear-host;
Write-Host 'This script will copy ' $numFiles ' files from ' $SourceFolder ' to ' $targetFolder
Read-host -prompt 'Press enter to start copying the files'

Get-ChildItem -Path $SourceFolder -Filter *.PDF | %{ 
    [System.IO.FileInfo]$destination = (Join-Path -Path $targetFolder -ChildPath $Name.Repository(".*","\"))

   if(!(Test-Path -Path $destination.Directory )){
    New-item -Path $destination.Directory.FullName -ItemType Directory 
    }
    [int]$percent = $i / $numFiles * 100

    copy-item -Path $_.FullName -Destination $Destination.FullName
    Write-Progress -Activity "Copying ... ($percent %)" -status $_  -PercentComplete $percent -verbose
    $i++
}
Write-Host 'Total number of files read from directory '$SourceFolder ' is ' $numFiles
Write-Host 'Total number of files that was copied to '$targetFolder ' is ' $i
Read-host -prompt "Press enter to complete..."
clear-host;
2

There are 2 best solutions below

0
On

Here is something you might try. The number for the directory is grabbed from a regex match, "(\d+)\..*.pdf". When you are confident that the correct file copies will be made, remove the -WhatIf from the Copy-Item cmdlet.

I did not try to address the Write-Progress capability. Also, this will only copy .pdf files that begin with digits followed by a FULL STOP (period) character.

I do not fully understand the need for all of the Write-Host and Read-Host usage. It is not very PowerShell. pwshic

$SourceFolder = 'C:/src/t/copymaps'
$targetFolder = 'C:/src/t/copymaps/base'

$i = 0
$numFiles = (
    Get-ChildItem -File -Path $SourceFolder -Filter "*.pdf" |
        Where-Object -FilterScript { $_.Name -match "(\d+)\..*.pdf" } |
        Measure-Object).Count

clear-host;
Write-Host 'This script will copy ' $numFiles ' files from ' $SourceFolder ' to ' $targetFolder
Read-host -prompt 'Press enter to start copying the files'

Get-ChildItem -File -Path $SourceFolder -Filter "*.pdf" |
    Where-Object -FilterScript { $_.Name -match "(\d+)\..*.pdf" } |
    ForEach-Object {
        $NumberDir = Join-Path -Path $targetFolder -ChildPath $Matches[1]
        $NumberDir = Join-Path -Path $NumberDir -ChildPath 'Repository'
        if (-not (Test-Path $NumberDir)) {
            New-Item -ItemType Directory -Path $NumberDir
        }

        Copy-Item -Path $_.FullName -Destination $NumberDir -Whatif
        $i++
    }

Write-Host 'Total number of files read from directory '$SourceFolder ' is ' $numFiles
Write-Host 'Total number of files that was copied to '$targetFolder ' is ' $i
Read-host -prompt "Press enter to complete..."
clear-host;
0
On

This should do mostly what you need. You might have to tweak the destination path a bit, but that should be fairly straight forward to figure out. I Highly recommend that use a '-' as the delimiter for your file prefix as opposed to a '.' as this will prevent accidentally moving EVERY FILE in a directory if you happen to execute it in the wrong place.

Also, when you write a script, do create functions to do individual units of work, and then call those functions at the end. It's much easier to modify, and debug that way.

<#
.SYNOPSIS
  Moves files from source to destination based on FileName
  Creates destination folder if it does not exist. 
.DESCIPTION
  The script expects files with a prefix defined by a hyphen '-' i.e. 200-<filename>.<ext>.
  There is no filename validation in this script; it will *probably* skip files without a prefix.
  A folder based on the prefix will be created in the destination. 
  If your file is name string-cheese.txt then it will be moved to $DestinationIn\string\string-cheese.txt
.PARAMETER SourceIn
  Source Path (folder) where your files exist.
.PARAMETER DestinationIn
  Target Path (folder) where you want your files to go.
.EXAMPLE
  & .\CleanUp-Files.ps1 -SourceIn "C:\Users\User\Documents\Files\" -DestinationIn "C:\Users\User\Documents\Backup\" -Verbose
.NOTES
  Author: RepeatDaily
  Email: [email protected]

  This script is provided as is, and will probably work as intended.  Good Luck!
  https://stackoverflow.com/questions/50662140/copy-file-based-a-specified-folder-based-on-file-name-create-folder-if-it-doesn
#>
[CmdletBinding()]
param (
  [string]$SourceIn,
  [string]$DestinationIn
)

function Set-DestinationPath {
  param (
    [string]$FileName,
    [string]$Target
  )
  [string]$NewParentFolderName = $FileName.SubString(0,$FileName.IndexOf('-'))
  [string]$DestinationPath = Join-Path -Path $Target -ChildPath $NewParentFolderName

  return $DestinationPath
}  

function Create-DestinationPath {
  [CmdletBinding()]
  param (
    [string]$Target
  )
  if (-not(Test-Path -Path $Target)) {
    Try {
      New-Item -ItemType Directory -Path $Target | Write-Verbose
    }
    catch {
      Write-Error $Error[0];
    }
  }
  else {
    Write-Verbose "$Target exists"
  }
}

function Move-MyFiles {
  [CmdletBinding()]
  param (
    [string]$Source,
    [string]$Destination
  )
  [array]$FileList = Get-ChildItem $Source -File | Select-Object -ExpandProperty 'Name'

  foreach ($file in $FileList) {
    [string]$DestinationPath = Set-DestinationPath -FileName $file -Target $Destination

    Create-DestinationPath -Target $DestinationPath

    try {
      Move-Item -Path (Join-Path -Path $Source -ChildPath $file) -Destination $DestinationPath | Write-Verbose
    }
    catch {
      Write-Warning $Error[0]
    }
  }
}

Move-MyFiles -Source $SourceIn -Destination $DestinationIn