GrumpyCrouton
GrumpyCrouton

Reputation: 8621

Doing operations on file names and directories with powershell

I'm attempting to do some specific operations with the names of folders and files in a set directory.

This is my powershell script:

Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("_","") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace(" ","") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("hp","HP ") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("HP","HP ") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("Hp","HP ") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("&"," & ") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("a","A") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("b","B") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("c","C") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("d","D") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("e","E") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("f","F") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("g","G") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("h","H") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("i","I") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("j","J") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("k","K") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("l","L") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("m","M") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("n","N") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("o","O") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("p","P") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("q","Q") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("r","R") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("s","S") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("t","T") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("u","U") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("v","V") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("w","W") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("x","X") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("y","Y") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("z","Z") }

I know, it's essentially a bunch of loops.

I had a couple of problems and questions:

  1. The above code does not work for replacing lowercase letters with uppercase letters (a-z), why is that? It also seems to not work all the time recursively either. E.G folder name HP 14-Z040wm, file inside is HP14-Z040WM
  2. How can I ultimately make this script better? As in shorter, faster etc.

(I tagged batch-file as well, as I would definitely accept (my first choice when I started making this) a batch file solution.

Upvotes: 0

Views: 878

Answers (2)

Bacon Bits
Bacon Bits

Reputation: 32170

First, only get items once, then process them once. Next, you can replace the character substitution with the ToUpper() method. Third, since all those methods return strings, you can just chain the methods together.

It's easy enough for files:

Get-ChildItem -Recurse -File | ForEach-Object {
    $NewName = $_.Name.ToUpper().Replace('_','').Replace(' ','').Replace('HP', 'HP ');
    Rename-Item -Path $_.FullName -NewName $NewName;
}

Folders, however, are a whole other ball of wax. You can't rename a folder path to the same name, and path names are case-insensitive. So neither Rename-Item nor Move-Item will work alone. You'll have to use a temporary folder name.

Here's code for both files and folders:

#Unique name for temp folder
$TempFolderName = [System.Guid]::NewGuid().Guid;

Get-ChildItem -Recurse | ForEach-Object {
    $NewName = $_.Name.ToUpper().Replace('_','').Replace(' ','').Replace('HP', 'HP ');
    if (!$_.PSIsContainer) {
        #Regular file
        Rename-Item -Path $_.FullName -NewName $NewName;
    }
    else {
        #Get the parent folder
        $ParentFolder = Split-Path $_.FullName;

        #Rename folder to temp name
        Rename-Item -Path $_.FullName -NewName (Join-Path $ParentFolder $TempFolderName);

        #Rename temp name to folder
        Rename-Item -Path (Join-Path $ParentFolder $TempFolderName) -NewName (Join-Path $ParentFolder $NewName);
    }
}

Upvotes: 3

Matt
Matt

Reputation: 46700

You should rename files first then folders. Trying to do both at the same time could invalidate other objects in the pipeline and get you errors. Also the repetition here can be saved with -replace "\s|_" and.ToUpper().-replace` is a regex function that we are using to replace all space and underscores with nothing. You can chain these as well so we are also going to replace HP at the start of the name and add in the space.

$path = "C:\Temp"
Get-ChildItem $path -Recurse -Directory | Rename-Item -NewName {($_.Name).ToUpper() -replace "\s|_"} -WhatIf
Get-ChildItem $path -Recurse -File | Rename-Item -NewName {($_.Name).ToUpper() -replace "\s|_"} -WhatIf

I have put -WhatIf on there for testing. If this code is only going to change the case of the entity names then it is going to give an error that the file/folder name needs to be different (folders in are case insensitive). So you would need to move it to a temporary folder first. Windows is not case sensitive so it will see you trying to name the object the same.

You need to be very careful running this code as it permanently changes folder names to temporary names. If the process fails you would have to manually fix the broken folders. Look for .bak folders in that case. My tests did work however so hopefully it is just a precaution. Run your own tests to be sure.

$path = "C:\Temp"

# Rename folders
Get-ChildItem $path -Recurse -Directory | ForEach-Object{
    # Save the current name
    $originalName = $_.Name
    # Make sure the temp name is unique.
    $temp = Rename-Item -Path $_.FullName -NewName "$originalName.bak" -PassThru
    # Rename using the saved name and proposed changes.
    $temp | Rename-Item -NewName ($originalName.ToUpper() -replace "\s|_" -replace "^HP","HP ")
} 

Get-ChildItem $path -Recurse -File | Rename-Item -NewName {$_.Name.ToUpper() -replace "\s|_" -replace "^HP","HP "}

Upvotes: 1

Related Questions