Home > Mobile >  Powershell - find files containing text and copy them to new folder, rename duplicates
Powershell - find files containing text and copy them to new folder, rename duplicates

Time:05-05

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
  • Related