Rename Part of File Name - powershell

I am looking to batch rename part of a pdf file using a csv file. I have a csv file with two columns, name and Newname. My pdf files have a naming convention of 222222_test (for example) and are located in the C:\TEST folder. In the csv file, 222222 is in the name column and Jonathan is in the Newname column.
The folder is really going to have hundreds of pdf documents whenever I can get this to work.
$csv = Import-Csv "C:\TEST\Book1.csv"
# location of your files
$files = get-childitem "C:\TEST\*.DOCX"
foreach($item in $CSV){
foreach($file in $files){
if($item.name -eq $file.basename){
rename-item $file.fullname -NewName "$($item.newname)$($file.extension)" -Verbose
}
}
}
I am looking for a way for the 222222 (only) to be changed to Jonathan so the pdf file would be Jonathan_test. I was able to use the code when the file name is only 222222 but when the pdf is 222222_test, the code is not working.

Give this a try, remove the WhatIf if it works for your files. Else, we'll need to see some sample data from the csv.
foreach ($item in $CSV) {
foreach ($file in $files) {
if ($item.name -eq $file.basename) {
Rename-Item $file.fullname -NewName $($file.FullName -replace $item.name, $item.newname) -WhatIf
}
}
}

With hundreds of CSV rows, it pays to build up a hashtable up front that maps old names to new names.
You then only need to loop once over the file names, performing a fast hashtable lookup in each iteration.
# Initialize the hashtable.
$ht = #{}
# Fill the hashtable, with the "name" column's values as the keys,
# and the "newname" columns as the values.
Import-Csv C:\TEST\Book1.csv |
ForEach-Object {
$ht.Add($_.name, $_.newname)
}
# Loop over the files and rename them based on the hashtable
Get-ChildItem C:\TEST\*.DOCX | Rename-Item -NewName {
$prefix = ($_.BaseName -split '_')[0] # Get prefix (before "_")
$newPrefix = $ht[$prefix] # Look up the prefix in the hashtable.
if ($newPrefix) { # Replace the prefix, if a match was found.
$newPrefix + $_.Name.Substring($prefix.Length)
}
else { # No replacement - output the original name, which is a no-op.
$_.Name
}
} -WhatIf
-WhatIf previews the renaming operations; remove it to perform actual renaming.

Related

Bulk renaming files with different extensions in order using powershell

is there a way to bulk rename items such that a folder with the items arranged in order would have their name changed into numbers with zero padding regardless of extension?
for example, a folder with files named:
file1.jpg
file2.jpg
file3.jpg
file4.png
file5.png
file6.png
file7.png
file8.jpg
file9.jpg
file10.mp4
would end up like this:
01.jpg
02.jpg
03.jpg
04.png
05.png
06.png
07.png
08.jpg
09.jpg
10.mp4
i had a script i found somewhere that can rename files in alphabetical order. however, it seems to only accepts conventionally bulk renamed files (done by selecting all the files, and renaming them such that they read "file (1).jpg" etc), which messes up the ordering when dealing with differing file extensions. it also doesn't seem to rename files with variations in their file names. here is what the code looked like:
Get-ChildItem -Path C:\Directory -Filter file* | % {
$matched = $_.BaseName -match "\((?<number>\d+)\)"
if (-not $matched) {break;}
[int]$number = $Matches["number"]
Rename-Item -Path $_.FullName -NewName "$($number.ToString("000"))$($_.Extension)"
}
If your intent is to rename the files based on the ending digits of their BaseName you can use Get-ChildItem in combination with Where-Object for filtering them and then pipe this result to Rename-Item using a delay-bind script block.
Needles to say, this code does not handle file collision. If there is more than one file with the same ending digits and the same extension this will error out.
Get-ChildItem -Filter file* | Where-Object { $_.BaseName -match '\d+$' } |
Rename-Item -NewName {
$basename = '{0:00}' -f [int][regex]::Match($_.BaseName, '\d+$').Value
$basename + $_.Extension
}
To test the code you can use the following:
#'
file1.jpg
file2.jpg
file3.jpg
file4.png
file5.png
file6.png
file7.png
file8.jpg
file9.jpg
file10.mp4
'# -split '\r?\n' -as [System.IO.FileInfo[]] | ForEach-Object {
$basename = '{0:00}' -f [int][regex]::Match($_.BaseName, '\d+$').Value
$basename + $_.Extension
}
You could just use the number of files found in the folder to create the appropriate 'numbering' format for renaming them.
$files = (Get-ChildItem -Path 'D:\Test' -File) | Sort-Object Name
# depending on the number of files, create a formating template
# to get the number of leading zeros correct.
# example: 645 files would create this format: '{0:000}{1}'
$format = '{0:' + '0' * ($files.Count).ToString().Length + '}{1}'
# a counter for the index number
$index = 1
# now loop over the files and rename them
foreach ($file in $files) {
$file | Rename-Item -NewName ($format -f $index++, $file.Extension) -WhatIf
}
The -WhatIf switch is a safety measure. With this, no file gets actually renamed, you will only see in the console what WOULD happen. Once you are content with that, remove the -WhatIf switch from the code and run again to rename all your files in the folder

Powershell Rename Files by CSV - Multiple same name match

So I found this powershell script to rename files in a dir based on the csv table. It works as long as there is only one file with the exact name in the table. Well I have multiple files matching the same name that need replacing based on the table, example: jackson_001.jpg jackson_002.jpg, ect.. and if jackson is in the table with a matching rename cell, it does not recognize. I have tried .baseline.length -4) and that does nothing. Do I have to do more than just truncate the basename?
$csv = Import-Csv "path\to\rename.csv"
# location of your files
$files = get-childitem "path\to\myimages"
foreach($item in $CSV){
foreach($file in $files){
if($item.oldName -eq $file.basename){
rename-item $file.fullname -NewName "$($item.MyNewName)_Preview_$($item.oldName)-$($item.MyNewName)$($file.extension)" -Verbose
}
}
}

Search and replace files and folders names with txt file support

I have many folders and inside these different files. Each folder and their children files have the same name and different extension, so in the ABC folder there are the ABC.png, ABC.prj, ABC.pgw files, in the DEF folder there are the DEF.png, DEF.prj, DEF.pgw files and so on.
With a script I have created a txt file with the list of png file names. Then I put in row 2 a new name for the name in row1, in row 4 a new name for the name in row 3, and so on.
Now I'm searching a powershell script that:
- scan all folder for the name in row 1 and replace it with name in row2
- scan all folder for the name in row 3 and replace it with name in row4 and so on
I have try with this below, but it doesn't work.
Have you some suggestions? Thank you
$0=0
$1=1
do {
$find=Get-Content C:\1\Srv\MapsName.txt | Select -Index $0
$repl=Get-Content C:\1\Srv\MapsName.txt | Select -Index $1
Get-ChildItem C:\1\newmaps -Recurse | Rename-Item -NewName { $_.name -replace $find, $repl} -verbose
$0=$0+2
$1=$1+2
}
until ($0 -eq "")
I believe there are several things wrong with your code and also the code Manuel gave you.
Although you have a list of old filenames and new filenames, you are not using that in the Get-ChildItem cmdlet, but instead try and replace all files it finds.
Using -replace uses a Regular Expression replace, that means the special character . inside the filename is regarded as Any Character, not simply a dot.
You are trying to find *.png files, but you do not add a -Filter with the Get-ChildItem cmdlet, so now it will return all filetypes.
Anyway, I have a different approach for you:
If your input file C:\1\Srv\MapsName.txt looks anything like this:
picture1.png
ABC_1.png
picture2.png
DEF_1.png
picture3.png
DEF_2.png
The following code will use that to build a lookup Hashtable so it can act on the files mentioned in the input file and leave all others unchanged.
$mapsFile = 'C:\1\Srv\2_MapsName.txt'
$searchPath = 'C:\1\NewMaps'
# Read the input file as an array of strings.
# Every even index contains the file name to search for.
# Every odd index number has the new name for that file.
$lines = Get-Content $mapsFile
# Create a hashtable to store the filename to find
# as Key, and the replacement name as Value
$lookup = #{}
for ($index = 0; $index -lt $lines.Count -1; $index += 2) {
$lookup[$lines[$index]] = $lines[$index + 1]
}
# Next, get a collection of FileInfo objects of *.png files
# If you need to get multiple extensions, remove the -Filter and add -Include '*.png','*.jpg' etc.
$files = Get-ChildItem -Path $searchPath -Filter '*.png' -File -Recurse
foreach ($file in $files) {
# If the file name can be found as Key in the $lookup Hashtable
$find = $file.Name
if ($lookup.ContainsKey($find)) {
# Rename the file with the replacement name in the Value of the lookup table
Write-Host "Renaming '$($file.FullName)' --> $($lookup[$find])"
$file | Rename-Item -NewName $lookup[$find]
}
}
Edit
If the input text file 'C:\1\Srv\MapsName.txt' does NOT contain filenames including their extension, change the final foreach loop into this:
foreach ($file in $files) {
# If the file name can be found as Key in the $lookup Hashtable
# Look for the file name without extension as it is not given in the 'MapsName.txt' file.
$find = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
if ($lookup.ContainsKey($find)) {
# Rename the file with the replacement name in the Value of the lookup table
# Make sure to add the file's extension if any.
$newName = $lookup[$find] + $file.Extension
Write-Host "Renaming '$($file.FullName)' --> '$newName'"
$file | Rename-Item -NewName $newName
}
}
Hope that helps
The problem in your snippet is that it never ends.
I tried it and it works but keeps looping forever.
I created a folder with the files a.txt, b.txt and c.txt.
And in the map.txt I have this content:
a.txt
a2.md
b.txt
b2.md
c.txt
c2.md
Running the following script I managed to rename every file to be as expected.
$0=0
$1=1
$find=Get-Content D:\map.txt | Select -Index $0
while($find) {
$find=Get-Content D:\map.txt | Select -Index $0
$repl=Get-Content D:\map.txt | Select -Index $1
if(!$find -Or !$repl) {
break;
}
Get-ChildItem D:\Files -Recurse | Rename-Item -NewName { $_.name -replace $find, $repl} -verbose
$0=$0+2
$1=$1+2
}

Copy file to multiple locations from CSV

I am currently scanning a directory and matching file names and then copying them to various file share locations based upon csv file. The CSV file should have 2 fields: the destination column = path for the copy, and the string Find column = to identify the file to be copied.
My CSV file is like this:
"matching file names" , "Destination"
"Don" , "c:\test\a"
"Quest" , "c:\test\b"
Currently it copies all files to all the locations.
Script:
$csv = Import-Csv -Path "C:\Temp\list.csv"
$filepath = 'C:\Temp\Source'
Get-ChildItem $filepath | foreach {
$criteria = $csv
$find = $csv | select -ExpandProperty find
$a = $_.FullName
foreach ($f in $find) {
if ($a -like "*$f*") {
foreach ($c in $criteria) {
Copy-Item $_.FullName $c.Destination
}
}
}
}
For one thing there's no column "find" in your CSV. I'm going to assume that the column you titled "matching file names" actually has the title "find".
Your problem is caused by the nested double loop in your code. You iterate over each pattern, then copy all files matching a given pattern to every destination from the CSV with the innermost loop.
Use just one loop for the CSV data (to keep the association between filter string and destination path intact), and the problem will disappear. I'd also recommend using the Contains() method instead of the -like operator, so you don't need to add wildcards to the filter string.
Get-ChildItem $filepath | ForEach-Object {
foreach ($filter in $csv) {
if ($f.Name.Contains($filter.find)) {
Copy-Item $_.FullName $filter.Destination
}
}
}

Powershell - Assigning unique file names to duplicated files using list inside a .csv or .txt

I have limited experience with Powershell doing very basic tasks by itself (such as simple renaming or moving files), but I've never created one that has the need to actually extract information from inside a file and apply that data directly to a file name.
I'd like to create a script that can reference a simple .csv or text file containing a list of unique identifiers and have it assign those to a batch of duplicated files (they all have the same contents) that share a slightly different name in the form of a 3-digit number appended as the prefix of a generic name.
For example, let's say my list of files are something like this:
001_test.txt
002_test.txt
003_test.txt
004_test.txt
005_test.txt
etc.
Then my .csv contains an alphabetical list of what I would like those to become:
Alpha.txt
Beta.txt
Charlie.txt
Delta.txt
Echo.txt
etc.
I tried looking at similar examples, but I'm failing miserably trying to tailor them to get it to do the above.
EDIT: I didn't save what I already modified, but here is the baseline script I was messing with:
$file_server = Read-Host "Enter the file server IP address"
$rootFolder = 'C:\TEMP\GPO\source\5'
Get-ChildItem -LiteralPath $rootFolder -Directory |
Where-Object { $_.Name -as [System.Guid] } |
ForEach-Object {
$directory = $_.FullName
(Get-Content "$directory\gpreport.xml") |
ForEach-Object { $_ -replace "99.999.999.999", $file_server } |
Set-Content "$directory\gpreport.xml"
# ... etc
}
I think this is to replace a string inside a file though. I need to replace the file name itself using a list from another file (that is not getting renamed), while not changing the contents of the files that are being renamed.
So you want to rename similar files with those listed in a text file. Ok, here's what you are going to need for my solution (alias listed in parenthesis): Get-Content (GC), Get-ChildItem (GCI), Where (?), Rename-Item, ForEach (%)
$NewNames = GC c:\temp\Namelist.txt #Path, including file name, to list of new names
$Name = "dog.txt" #File name without the 001_ prefix
$Path = "C:\Temp" #Path to search
$i=0
GCI $path | ?{$_.Name -match "\d{3}_$Name"}|%{Rename-Item $_.FullName $NewNames[$i];$i++}
Tested as working. That gets your list of new names and saves it as an array. Then it defines your file name, path, and sets $i to 0 as a counter. Then for each file that matches your pattern it renames it based off of item number $i in the array of new names, and then increments $i up one number and moves to the next file.
I haven't tested this, but it should be pretty close. It assumes you have a CSV with a column named FileNames and that you have at least as many names in that list as there are on disk.
$newNames = Import-Csv newfilenames.csv | Select -ExpandProperty FileNames
$existingFiles = Get-ChildItem c:\someplace
for ($i = 0; $i -lt $existingFiles.count; $i++)
{
Rename-Item -Path $existingFiles[$i].FullName -NewName $newNames[$i]
}
Basically, you create two arrays and using a basic for loop steping through the list of files on disk and pull the name from the corresponding index in the newNames array.
Does your CSV file map the identifiers to the file names?
Identifier,NewName
001,Alpha
002,Beta
If so, you'll need to look up the identifier before renaming the file:
# Define the naming convention
$Suffix = '_test'
$Extension = 'txt'
# Get the files and what to rename them to
$Files = Get-ChildItem "*$Suffix.$Extension"
$Csv = Import-Csv 'Names.csv'
# Rename the files
foreach ($File in $Files) {
$NewName = ($Csv | Where-Object { $File.Name -match '^' + $_.Identifier } | Select-Object -ExpandProperty NewName)
Rename-Item $File "$NewName.$Extension"
}
If your CSV file is just a sequential list of filenames, logicaldiagram's answer is probably more along the lines of what you're looking for.