I am writing an app in Powershell. The app serves to find files in source location by string in filename. Files that are found must be copied to a new location.
I expect there might be duplicates and it there are a number must be added at the end of the filename.
In the end the searched string must be replaced with new one (but I got that covered already)
I found a script that does exactly what I want but for all files in location, but doesn't work with searching exact string. I tried writing one, but have problems with duplicates... Help apriciated!
#### Function to check if duplicates exist and copy files ####
function fcopy ($SourceDir,$DestinationDir)
{
Get-ChildItem $SourceDir -Recurse | Where-Object { $_.PSIsContainer -eq $false } | ForEach-Object ($_) {
$SourceFile = $_.FullName
$DestinationFile = $DestinationDir $_
if (Test-Path $DestinationFile) {
$i = 0
while (Test-Path $DestinationFile) {
$i = 1
$DestinationFile = $DestinationDir $_.basename $i $_.extension
}
} else {
Copy-Item -Path $SourceFile -Destination $DestinationFile -Verbose -Force
}
Copy-Item -Path $SourceFile -Destination $DestinationFile -Verbose -Force
}
}
##### Call fcopy function ######
fcopy -SourceDir $source -DestinationDir $destination
CodePudding user response:
You can use the function below. It will copy files that match a -Filter
pattern and appends a sequence number to its name if a file with duplicate name is already present in the destination folder:
function Copy-Unique {
# Copies files to a destination. If a file with the same name already exists in the destination,
# the function will create a unique filename by appending '(x)' after the name, but before the extension.
# The 'x' is a numeric sequence value.
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'ByPath')] # add support for -WhatIf switch
Param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = 'ByPath')]
[Alias("Source")]
[ValidateScript({Test-Path -Path $_ -PathType Container})]
[string]$Path,
[Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = 'ByLiteralPath')]
[ValidateScript({Test-Path -LiteralPath $_ -PathType Container})]
[string]$LiteralPath,
[Parameter(Mandatory = $true, Position = 1)]
[string]$Destination,
[Parameter(Mandatory = $false)]
[string]$Filter = '*', # by default, filter allows any file pattern
[switch]$Recurse
)
# create the destination path if it does not exist
if (!(Test-Path -Path $Destination -PathType Container)) {
Write-Verbose "Creating folder '$Destination'"
$null = New-Item -Path $Destination -ItemType 'Directory' -Force
}
# get a list of file FullNames in this source folder
if ($PSCmdlet.ParameterSetName -eq 'ByLiteralPath') {
$sourceFiles = @(Get-ChildItem -LiteralPath $LiteralPath -Filter $Filter -File -Recurse:$Recurse)
}
else {
$sourceFiles = @(Get-ChildItem -Path $Path -Filter $Filter -File -Recurse:$Recurse)
}
foreach ($file in $sourceFiles) {
# get an array of all filenames (names only) of the files with a similar name already present in the destination folder
$destFiles = @((Get-ChildItem $Destination -File -Filter "$($file.BaseName)*$($file.Extension)").Name)
# for PowerShell version < 3.0 use this
# $destFiles = @(Get-ChildItem $Destination -Filter "$baseName*$extension" | Where-Object { !($_.PSIsContainer) } | Select-Object -ExpandProperty Name)
# construct the new filename
$newName = $file.Name
$count = 1
while ($destFiles -contains $newName) {
$newName = "{0}({1}){2}" -f $file.BaseName, $count , $file.Extension
}
# use Join-Path to create a FullName for the file
$newFile = Join-Path -Path $Destination -ChildPath $newName
Write-Verbose "Copying '$($file.FullName)' as '$newFile'"
$file | Copy-Item -Destination $newFile -Force
}
}
# you can change the folder paths, file pattern to filter etc. here
Copy-Unique -Path 'D:\Test' -Destination 'D:\Destination' -Filter '*.ps1' -Recurse