Home > other >  Find similarly-named files, and if present, remove the files without a specific string using PowerSh
Find similarly-named files, and if present, remove the files without a specific string using PowerSh

Time:01-31

In a directory, there are files with the following filenames:

ExampleFile.mp3
ExampleFile_pn.mp3
ExampleFile2.mp3
ExampleFile2_pn.mp3
ExampleFile3.mp3

I want to iterate through the directory, and IF there is a filename that contains the string '_pn.mp3', I want to test if there is a similarly named file without the '_pn.mp3' in the same directory. If that file exists, I want to remove it.

In the above example, I'd want to remove:

ExampleFile.mp3
ExampleFile2.mp3

and I'd want to keep ExampleFile3.mp3

Here's what I have so far:

$pattern = "_pn.mp3"
$files = Get-ChildItem -Path '$path' | Where-Object {! $_.PSIsContainer}
Foreach ($file in $files) {
    If($file.Name -match $pattern){
        # filename with _pn.mp3 exists
        Write-Host $file.Name
        # search in the current directory for the same filename without _pn
        <# If(Test-Path $currentdir $filename without _pn.mp3) {
           Remove-Item -Force}
        #>

}
enter code here

CodePudding user response:

You could use Group-Object to group all files by their BaseName (with the pattern removed), and then loop over the groups where there are more than one file. The result of grouping the files and filtering by count would look like this:

$files | Group-Object { $_.BaseName.Replace($pattern,'') } |
Where-Object Count -GT 1

Count Name                      Group
----- ----                      -----
    2 ExampleFile               {ExampleFile.mp3, ExampleFile_pn.mp3}
    2 ExampleFile2              {ExampleFile2.mp3, ExampleFile2_pn.mp3}

Then if we loop over these groups we can search for the files that do not end with the $pattern:

@'
ExampleFile.mp3
ExampleFile_pn.mp3
ExampleFile2.mp3
ExampleFile2_pn.mp3
ExampleFile3.mp3
'@ -split '\r?\n' -as [System.IO.FileInfo[]] | Set-Variable files

$pattern = "_pn"
$files | Group-Object { $_.BaseName.Replace($pattern,'') } |
Where-Object Count -GT 1 | ForEach-Object {
    $_.Group.Where({-not $_.BaseName.Endswith($pattern)})
}

This is how your code would look like, remove the -WhatIf switch if you consider the code is doing what you wanted.

$pattern = "_pn.mp3"
$files = Get-ChildItem -Path -Filter *.mp3 -File
$files | Group-Object { $_.BaseName.Replace($pattern,'') } |
Where-Object Count -GT 1 | ForEach-Object {
    $toRemove = $_.Group.Where({-not $_.BaseName.Endswith($pattern)})
    Remove-Item $toRemove -WhatIf
}

CodePudding user response:

I think you can get by here by adding file names into a hash map as you go. If you encounter a file with the ending you are interested in, check if a similar file name was added. If so, remove both the file and the similar match.

$ending = "_pn.mp3"
$files = Get-ChildItem -Path $path -File | Where-Object { ! $_.PSIsContainer }
$hash = @{}
Foreach ($file in $files) {
    # Check if file has an ending we are interested in
    If ($file.Name.EndsWith($ending)) {
        $similar = $file.Name.Split($ending)[0]   ".mp3"

        # Check if we have seen the similar file in the hashmap
        If ($hash.Contains($similar)) {
            Write-Host $file.Name
            Write-Host $similar
            Remove-Item -Force $file
            Remove-Item -Force $hash[$similar]

            # Remove similar from hashmap as it is removed and no longer of interest
            $hash.Remove($similar)
        }
    }
    else {
        # Add entry for file name and reference to the file
        $hash.Add($file.Name, $file)
    }
}

CodePudding user response:

Just get a list of the files with the _pn then process against the rest.

$pattern = "*_pn.mp3"
$files = Get-ChildItem -Path "$path" -File  -filter "$pattern"

Foreach ($file in $files) {

    $TestFN = $file.name -replace("_pn","")
    If (Test-Path -Path $(Join-Path -Path $Path -ChildPath $TestFN)) {
      $file | Remove-Item -force
    }

} #End Foreach
  •  Tags:  
  • Related