Home > Software design >  Rename files in a bulk from csv file in Powerhshell
Rename files in a bulk from csv file in Powerhshell

Time:01-18

Renaming files from a CSV

I am trying to write a script to reorganize files in a folder structure using csv as index file but I can´t figure out in how to solve the Rename-Item error.

Questions

  1. Is there others way to write this script in order to achieve the same results more easily?
  2. How to pass the right parameters to Rename-Item?

My csv file template

folderName                          newName                                      oldName          
----------                          -------                                      -------          
01 Course Overview                  01_Course_Overview                           1280x720.mp4     
02 Introduction to PowerShell       01_Introduction to PowerShell                1280x720 (1).mp4 
02 Introduction to PowerShell       02_Who Is This Course For?                   1280x720 (2).mp4 
02 Introduction to PowerShell       03_What Is PowerShell?                       1280x720 (3).mp4 
02 Introduction to PowerShell       04_Windows PowerShell and PowerShell 7       1280x720 (4).mp4 

PowerShell Script

$csv = Import-Csv '.\index.csv' -Delimiter ';'
$newFolders = $csv.folderName | Sort-Object -Unique
$listFolders = Get-ChildItem -Directory | Select-Object Name
$listFiles = Get-ChildItem | Where {$_.extension -eq ".mp4"}

ForEach ($a in $newFolders){
    
    If ($listFolders.Name -contains $a){
        Write-Host "The Folder $a exist"
    }
    else{
        New-Item -Path $pwd.Path -Name $a -Type Directory | Out-Null
        Write-Host "The folder $a has been created"
    }

}

ForEach ($b in $csv){
    
    If ($listFiles.Name -contains $b.oldName){
        Write-Host "File $($b.oldName) exist"
        Write-Host "Renaming file to: "$($b.newName)"
        
        #Rename-Item $($b.oldName) -NewName $($b.newName)
        #Write-Host "Moving file to: "$($b.folderName)"
        #Move-Item .\$($b.newName) -Destination .\$($b.folderName)
    }
    else{
        Write-Host "File $($b.oldName) doesn't exist" `n
    }
    
}

Error when executin Rename-Item

No D:\Downloads\Pluralsight\_PowerShell_Essentials\01_Powershell_Getting_Started\Temp\indexfiles.ps1:30 caractere:9
          Rename-Item $($b.oldName) -NewName $($b.newName)
          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      CategoryInfo          : InvalidArgument: (D:\Downloads\Pl...280x720 (2).mp4:String) [Rename-Item], ArgumentException
      FullyQualifiedErrorId : RenameItemArgumentError,Microsoft.PowerShell.Commands.RenameItemCommand

CodePudding user response:

Here you have an example of how this can be done, this is mainly a test case, it will create the files as you show us on the CSV and move them to the new folders based on the folderName column.

The code will look for the files on the current directory, before testing it with the real files, Set-Location (cd) to that folder.

If you're not sure if the code will work you can add a -WhatIf switch to Rename-Item and Move-Item.

Note, I have removed ? from the newName column since it's an invalid character on Windows. See this answer for more details.

# Go to a temporary folder for testing
Set-Location path/to/temporaryfolder/here

# Here you would use:
# $csv = Import-Csv path/to/csv.csv
$csv = @'
folderName                          newName                                      oldName
01 Course Overview                  01_Course_Overview                           1280x720.mp4
02 Introduction to PowerShell       01_Introduction to PowerShell                1280x720 (1).mp4
02 Introduction to PowerShell       02_Who Is This Course For                    1280x720 (2).mp4
02 Introduction to PowerShell       03_What Is PowerShell                        1280x720 (3).mp4
02 Introduction to PowerShell       04_Windows PowerShell and PowerShell 7       1280x720 (4).mp4
'@ -replace '   ',',' | ConvertFrom-Csv

# Create test files, this part is only for testing the code
$csv.foreach({ New-Item $_.oldName -ItemType File })

foreach($line in $csv)
{
    if(-not (Test-Path $line.folderName))
    {
        # Create the Folder if it does not exist
        New-Item $line.folderName -ItemType Directory -Verbose
    }
    Rename-Item -LiteralPath $line.oldName -NewName $line.newName
    Move-Item -LiteralPath $line.newName -Destination $line.folderName
}

CodePudding user response:

If I understand correctly, your real CSV file contains folder and/or file names with characters that are invalid like the ?.

To fix that, you can choose to remove those characters from the CSV file first, OR make sure you remove them before creating a folder or renaming a file.
For both options, you can use this small helper function:

function Remove-InvalidNameChars {
    param(
        [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [String]$Name,

        [ValidateSet('File', 'Path')]
        [string]$Type ='File'
    )

    if ($Type -eq 'File') {
        $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''
    }
    else {
        $invalidChars = [IO.Path]::GetInvalidPathChars() -join ''
    } 
    # build a regex string from the invalid characters
    $removeThese = "[{0}]" -f [RegEx]::Escape($invalidChars)
    # output the name with invalid characters removed
    $Name -replace $removeThese
}

Method 1: remove the invalid characters from the CSV file and use cleaned-up data:

$sourcePath = 'D:\Test'
$csvFile    = Join-Path -Path $sourcePath -ChildPath 'index.csv'
$csvData    = Import-Csv -Path $csvFile -Delimiter ';'
foreach ($item in $csvData) {
    $item.folderName = Remove-InvalidNameChars -Name $item.folderName -Type Path
    $item.newName    = Remove-InvalidNameChars -Name $item.newName -Type File
}
$csvData | Export-Csv -Path $csvFile -Delimiter ';' -Force  # rewrite the CSV file if you like

# now use the cleaned-up data in $csvData for the rest of the code:
foreach ($item in $csvData) {
    # create the output folder if this does not already exist
    $targetPath = Join-Path -Path $sourcePath -ChildPath $item.folderName
    $null = New-Item -Path $targetPath -ItemType Directory -Force
    # move and rename the file if found
    $sourceFile = Join-Path -Path $sourcePath -ChildPath $item.oldName
    if (Test-Path -Path $sourceFile -PathType Leaf) {
        $targetFile = Join-Path -Path $targetPath -ChildPath $item.newName
        Move-Item -Path $sourceFile -Destination $targetFile
    }
}

Method 2: leave the csv data as-is and make sure you remove invalid characters while renaming/moving:

$sourcePath = 'D:\Test'
$csvFile    = Join-Path -Path $sourcePath -ChildPath 'index.csv'
$csvData    = Import-Csv -Path $csvFile -Delimiter ';'
foreach ($item in $csvData) {
    # create the output folder if this does not already exist
    $targetPath = Join-Path -Path $sourcePath -ChildPath (Remove-InvalidNameChars -Name $item.folderName -Type Path)
    $null = New-Item -Path $targetPath -ItemType Directory -Force
    # move and rename the file if found
    $sourceFile = Join-Path -Path $sourcePath -ChildPath (Remove-InvalidNameChars -Name $item.oldName -Type File)
    if (Test-Path -Path $sourceFile -PathType Leaf) {
        $targetFile = Join-Path -Path $targetPath -ChildPath (Remove-InvalidNameChars -Name $item.newName -Type File)
        Move-Item -Path $sourceFile -Destination $targetFile
    }
}

Note that Move-Item can move a file to a new destination and rename it at the same time, so you do not need Rename-Item


P.S. I noticed in your example CSV there are no extensions to the newName filenames..
If that is the case in real life, you need to add these aswell.

For that change the Move-Item line to:

Move-Item -Path $sourceFile -Destination ([IO.Path]::ChangeExtension($targetFile, [IO.Path]::GetExtension($sourceFile)))
  •  Tags:  
  • Related