Powershell: найдите папки с определенным именем и переместите содержимое на один уровень вверх, переименуйте, если они существуют

У меня есть первые две трети этой работы, но я застрял на последней части. У меня есть скрипт, который ищет вложенные папки с определенным именем и перемещает их содержимое на один уровень вверх. У меня есть другой скрипт, который перемещает файлы из одного места в другое и переименовывает их, если файл уже существует. То, что я пытаюсь сделать сейчас, это объединить два. Итак, вот тот, который перемещает файлы вверх:

$sourceDir = "E:\Deep Storage"
$searchFolder = "Draft Materials"

Get-ChildItem  -path $sourceDir -filter $searchFolder -Recurse | 
    ForEach-Object {
        Get-ChildItem -File -Path $_.FullName |
            ForEach-Object {
                Move-Item -Path $_.FullName -Destination $(Split-Path -Parent $_.PSParentPath)
            }
    }

А вот тот, который перемещает вещи при переименовании, если они уже существуют:

$sourceDir = "E:\Test1"
$targetDir = "E:\Deep Storage\Test1"

Get-ChildItem -Path $sourceDir -Filter *.* -Recurse | ForEach-Object {
    $num=1
    $nextName = Join-Path -Path $targetDir -ChildPath $_.name

    while(Test-Path -Path $nextName)
    {
       $nextName = Join-Path $targetDir ($_.BaseName + "_$num" + $_.Extension)    
       $num+=1   
    }

    $_ | Copy-Item -Destination $nextName -Verbose
}

И, наконец, моя попытка скрестить два:

$sourceDir = "E:\Deep Storage"
$searchFolder = "Draft Materials"

Get-ChildItem  -path $sourceDir -filter $searchFolder -Recurse | 
    ForEach-Object {
        Get-ChildItem -File -Path $_.FullName |
            ForEach-Object {
    $num=1
    $nextName = Join-Path -Path $_.FullName -Destination $(Split-Path -Parent $_.PSParentPath)

    while(Test-Path -Path $nextName)
    {
       $nextName = Join-Path -Path $_.FullName -Destination $(Split-Path -Parent $_.PSParentPath) ($_.BaseName + "_$num" + $_.Extension)    
       $num+=1   
    }

    $_ | Copy-Item -Destination $nextName
}
    }

Я чувствую, что нахожусь на правильном пути, но после двух часов попыток я не смог заставить это работать.

Обновлено: предоставление точного синтаксиса, который я даю

$sourceDir = "E:\Deep Storage\Projects"
$searchFolder = "Draft Materials"
$destinationPath = "$($sourceDir)\.."

Write-Host "OPERATION: Search for Folders Named" -ForegroundColor White -BackgroundColor DarkGreen -NoNewLine;
Write-Host " '$searchFolder' " -ForegroundColor Yellow -BackgroundColor DarkGreen -NoNewLine;
Write-Host "and Move Contents Up One Level" -ForegroundColor White -BackgroundColor DarkGreen;
Write-Host "SEARCHDIR: $sourceDir" -ForegroundColor White -BackgroundColor DarkGreen;

# Get all directories in specific folders inside the source directory
$folders = Get-ChildItem -Path $sourceDir -Directory | Where-Object {$_.Name -like "$searchFolder*" -or $_.FullName -like "*\$searchFolder\*"}

foreach ($folder in $folders) {
    # Get all files in the current folder
    $files = Get-ChildItem -Path $folder.FullName

    foreach ($file in $files) {
        $destinationFile = Join-Path -Path $destinationPath -ChildPath $file.Name
        if (Test-Path $destinationFile) {
            # If a file with the same name exists in the destination directory, rename it
            $name = $file.Name
            $extension = $file.Extension
            $i = 0
            while (Test-Path $destinationFile) {
                $i++
                $name = "{0}_{1}{2}" -f ($file.BaseName, $i, $extension)
                $destinationFile = Join-Path -Path $destinationPath -ChildPath $name
            }
            Write-Host "Renaming $($file.Name) to $name"
        }
        Move-Item $file.FullName $destinationFile -Verbose -WhatIf
    }

}

🤔 А знаете ли вы, что...
PowerShell поддерживает создание пользовательских функций и модулей для повторного использования кода.


73
2

Ответы:

Пожалуйста, попробуйте это:

$sourcePath = "C:\temp\test\Test"
$destinationPath = "$($sourcePath)\.."

# Get all files in the source directory
$files = Get-ChildItem -Path $sourcePath

foreach ($file in $files) {
    $destinationFile = Join-Path -Path $destinationPath -ChildPath $file.Name
    if (Test-Path $destinationFile) {
        # If a file with the same name exists in the destination directory, rename it
        $name = $file.Name
        $extension = $file.Extension
        $i = 0
        while (Test-Path $destinationFile) {
            $i++
            $name = "{0}_{1}{2}" -f ($file.BaseName, $i, $extension)
            $destinationFile = Join-Path -Path $destinationPath -ChildPath $name
        }
        Write-Host "Renaming $($file.Name) to $name"
    }
    Move-Item $file.FullName $destinationFile
}

Это не удалит исходное местоположение и переместит все из sourcePath в родительское местоположение. Чтобы удалить исходное местоположение, просто добавьте в конце:

Remove-Item -Path $sourcePath -Force -Confirm:$false

ОБНОВЛЕНИЕ1:

$sourcePath = "C:\temp\test\Test"
$destinationPath = "$($sourcePath)\.."

# Get all directories in specific folders inside the source directory
$folders = Get-ChildItem -Path $sourcePath -Directory | Where-Object {$_.Name -like "Folder1*" -or $_.FullName -like "*\Folder2\*"}

foreach ($folder in $folders) {
    # Get all files in the current folder
    $files = Get-ChildItem -Path $folder.FullName

    foreach ($file in $files) {
        $destinationFile = "$($folder.FullName)\.."
        if (Test-Path $destinationFile) {
            # If a file with the same name exists in the destination directory, rename it
            $name = $file.Name
            $extension = $file.Extension
            $i = 0
            while (Test-Path $destinationFile) {
                $i++
                $name = "{0}_{1}{2}" -f ($file.BaseName, $i, $extension)
                $destinationFile = Join-Path -Path $destinationPath -ChildPath $name
            }
            Write-Host "Renaming $($file.Name) to $name"
        }
        Move-Item $file.FullName $destinationFile
    }

}

Решено

Вот что я придумал, прочитав пост, код и комментарии OP:

$sourcePath      = 'E:\Deep Storage\Projects'   
$searchFolder    = 'Draft Materials'

Write-Host "OPERATION: Search for Folders Named" -ForegroundColor White -BackgroundColor DarkGreen -NoNewLine;
Write-Host " '$searchFolder' " -ForegroundColor Yellow -BackgroundColor DarkGreen -NoNewLine;
Write-Host "and Move Contents Up One Level" -ForegroundColor White -BackgroundColor DarkGreen;
Write-Host "SEARCHDIR: $sourcePath" -ForegroundColor White -BackgroundColor DarkGreen;

# Get all directories in specific folders inside the source directory
$folders = (Get-ChildItem -LiteralPath $sourcePath -Directory -Recurse) | Where-Object Name -match $searchFolder

### Check selected folders
    $Folders.FullName | Out-GridView -Title 'Selected Folders'
    Read-Host 'Paused. Check selected folders in GridView. Press <enter> to continue '
###

ForEach ($folder in $folders)
{
    # Get all files in the current folder
    $filesToCopy   = $folder | Get-ChildItem -File
    # Get list of names for exising files in target (parent folder)
    $targetPath    = $folder.Parent.FullName
    $filesInTarget = (Get-ChildItem -LiteralPath $targetPath -File).Name

    ForEach ($file in $filesToCopy)
    {
        If ($file.Name -notIn $filesInTarget)
        {
            $file | Move-Item -Destination $targetPath -Verbose -WhatIf
        }
        Else
        {
            $i = 0
            Do
            {
                $newName = '{0}_{1}{2}' -f ($file.BaseName, $i++, $file.Extension)
            } Until ( $newName -notIn $FilesInTarget )

            Write-Host ('Renaming "{0}" to "{1}"...' -f $file.Name , $newName)

            $file | Move-Item -Destination (Join-Path $targetPath $newName) -Verbose -WhatIf
        }
    }
    # Delete (hopefully empty) folder
    If (!($folder | Get-ChildItem -Force))
    {
        $folder | Remove-Item -WhatIf
    }
    Else
    {
        Write-Host ('Items still exist in "{0}". Folder not deleted.' -f $folder.FullName)
    }
}
  • Выбор синтаксиса: для любого командлета с наборами параметров Path/LiteralPath (gci, Copy, Move, Rename и т. д.) синтаксис System.IO.FileSystemInfo | <Cmdlet> работает с элементами, которые потерпели бы неудачу в форме <Cmdlet> -Path (System.IO.FileSystemInfo).FullNaame, потому что для специальных символов в их имени требуется параметр -LiteralPath. .

    Во многих случаях замена -Path на -LiteralPath (или его псевдоним: -lp) также будет работать. Но конвейерный формат читается как «чище» (ИМХО) при сканировании кода и, если вы только изучаете PowerShell, напоминает вам о необходимости мыслить с точки зрения конвейеризации, когда это возможно, и избегать промежуточных переменных. Просто для улыбки, вот версия приведенного выше кода, в которой элементы передаются как можно чаще с использованием ForEach-Object:

     (Get-ChildItem -LiteralPath $sourcePath -Directory -Recurse) |
         where Name -match $searchFolder |
     ForEach-Object {
         # Get list of names for exising files in target (parent folder)
         $targetPath    = $_.Parent.FullName
         $filesInTarget = (Get-ChildItem -LiteralPath $targetPath -File).Name
    
         $_ | Get-ChildItem -File | ForEach-Object {
             If ($_.Name -notIn $filesInTarget)
             {
                 $_ | Move-Item -Destination $targetPath -Verbose -WhatIf
             }
             Else
             {
                 $i = 0
                 Do
                 {
                     $newName = '{0}_{1}{2}' -f ($_.BaseName, $i++, $_.Extension)
                 } Until ( $newName -notIn $FilesInTarget )
    
                 Write-Host ('Renaming "{0}" to "{1}"...' -f $_.Name , $newName)
    
                 $_ | Move-Item -Destination (Join-Path $targetPath $newName) -Verbose -WhatIf
             }
         }
         # Delete (hopefully empty) folder
         If (!($_ | Get-ChildItem -Force))
         {
             $_ | Remove-Item -WhatIf
         }
         Else
         {
             Write-Host ('Items still exist in "{0}". Folder not deleted.' -f $_.FullName)
         }
     }