Home > Blockchain >  PowerShell search recursively for files and folder which the name contains "..."
PowerShell search recursively for files and folder which the name contains "..."

Time:09-21

I need help to create a script that can save my life.

My backup software got it wrong because I misdesigned my backup plan and I have lots of named files; filename or folder name (2) (encoding conflict).

I would like to recursively search my network share to find folders and files with "(encode conflict)" in their name and first export them for verification.

Then, if all goes well, I would like to move them to another place while keeping the folder's hierarchy and that's where I stuck.

Get-ChildItem -LiteralPath '\\?\E:\Network Shares\Commun' -Recurse  -Filter "*(encode*" #| move-item -Destination 'C:\Users\Desktop\Conflits\'

For the export I found the script here :

https://stackoverflow.com/a/15261816/19493679

Thanks to OP

The files from my test are moving without folder's and file's hierarchy ...

Can you help me please ? :)

CodePudding user response:

Move-Item doesn't know about the directory structure, it just gets paths fed one by one from Get-ChildItem. For preserving directory structure, Move-Item would have to know a common base path, but there is currently no way to specify one.

So I've created a helper function New-DestinationPath that can be chained in between Get-ChildItem and Move-Item (or Copy-Item). The function creates the destination directory structure and outputs the source path and the fully resolved destination path.

Function New-DestinationPath {
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('Fullname', 'PSPath')]
        [String] $Path,

        [Parameter(Mandatory, Position=0)]
        [String] $CommonPath,

        [Parameter(Mandatory, Position=1)]
        [String] $Destination
    )
    process {
        # Temporarily change current directory -> base directory for Resolve-Path -Relative
        Push-Location $CommonPath
        try {
            # Resolve input path relative to $CommonPath (current directory)  
            $relativePath = Resolve-Path $Path -Relative
        }
        finally {
            Pop-Location  # Restore current directory
        }

        # Resolve full target file path and directory
        $targetPath   = Join-Path $Destination $relativePath
        $targetDir    = Split-Path $targetPath -Parent

        # Create target dir if not already exists (-Force).
        $null = New-Item $targetDir -ItemType Directory -Force
       
        # Output the full source and destination paths (for use with Copy-Item or Move-Item) 
        [PSCustomObject]@{
            Path = $Path
            Destination = $targetPath
        }
    }
}

Usage:

$searchPath = '\\?\E:\Network Shares\Commun'
Get-ChildItem -LiteralPath $searchPath -Recurse -File |
  Where-Object Fullname -like '*(encode*' |
  New-DestinationPath -CommonPath $searchPath -Destination 'C:\Users\Desktop\Conflits' -WhatIf |
  Move-Item -LiteralPath { $_.Path } -Destination { $_.Destination } -WhatIf
  • By using the common parameter -WhatIf the functions only output what they would do. Remove both -WhatIf arguments when you have confirmed that the right paths are used.
  • In the Move-Item call we are using delay-bind scriptblocks to avoid a ForEach-Object command. Using the automatic variable $_ we can directly refer to the output of the previous command in the pipeline (New-DestinationPath).
  • Related