Renaming a file name to exclude the first couple parts Powershell - powershell

I have over a million files like such: First_Last_MI_DOB_ and lots more information. Is there a way I can run a rename script that can remove just the first, last, Mi, and DOB from the file name, but keep the stuff after that? Thank you.

Edited from my answer to this question: Parse and Switch Elements of Folder Names using Powershell
# Path to folder
$Path = '.\'
# Regex to match "ID_000000..."
$Regex = 'ID_\d+.*$'
# Get all objects in path
Get-ChildItem -Path $Path |
# Select only objects that are not directory and name matches regex
Where-Object {!$_.PSIsContainer -and $_.Name -match $Regex} |
# For each such object
ForEach-Object {
# Rename object
Rename-Item -Path $_.FullName -NewName $Matches[0]
}
UPDATE #1 : It seems that you need to write a regex that will match a required part of the name and then use it in to rename a document.
Assuming that file name is x-John_Doe_._DOB_01-11-1990_M_ID_000000_TitleofDocument_DateofDocument_Docpagenu‌​mber_, here is couple of the examples:
Regex (https://regex101.com/r/gI0fZ2/2): (ID_\d+.*)$ - will match ID_{ONE_OR_MORE_DIGITS}{ANY_CHARACTERS}
Result:ID_000000_TitleofDocument_DateofDocument_Docpagenu‌​mber_
Regex (https://regex101.com/r/gI0fZ2/1): \d{4}_(M|F)_(.*)$ - will match {4_DIGITS}_M_{or}_F_ and capture everything after that in capture group.
Result:
1st match - M
2nd match (the one to use) - ID_000000_TitleofDocument_DateofDocument_Docpagenu‌​mber_
UPDATE #2:
All the names in each file are different, a long with different ID's.
For example: John_Doe_DOB_01/01/01_ID_000000 and the next file name
could be: John_Smith_DOB_01/02/01_ID_100000 and so on. I am thinking I
would just want to read the file name in as a string, split it by _
and then make the new file name the stuff from [4] and after. Is there
a way to do that?
Sure, you can do that, but I'd recommend a regex approach, because it would work for every filename that has ID_0xxxx string, no matter of what. I've modified my initial example with first regex, so it should work for you.
But if you'd like to try splitting approach, here is how to do it:
# Path to folder
$Path = '.\'
# Filename separator
$Separator = '_'
# Get all objects in path
Get-ChildItem -Path $Path |
# Select only objects that are not directory and name matches regex
Where-Object {!$_.PSIsContainer} |
# For each such object
ForEach-Object {
# Generate new name
$NewName = ($_.Name -split $Separator | Select-Object -Skip 4) -join $Separator
# Rename object
Rename-Item -Path $_.FullName -NewName $NewName
}

Related

Delete everything in a file name BEFORE the last occurrence of an Underscore in Powershell?

I have tens of thousands of files which will need to be renamed in large batches after being manually manipulated. All of the files will have a barcode followed by a unique identifying string of alphanumerics. They will all have the same .PDF extension as well. All files should have an underscore before the unique identifier.
For example: 12 44571 522110_10_E-354-98-U90368.ENG
or : 14 44571 500169_9_Monroe NE_G-462-02-5674.GER
What I need to keep after renaming these files is the unique identifier which looks like this:
E-354-98-U90368.ENG
G-462-02-5674.GER
I am not versed in Powershell and have found a 2-step process that works, but if there is an error in the file name, to begin with, it deletes the extension and the important unique identifier above.
The two-step process is this. 1st, get rid of the barcode and # after it for each file. If it does not have a name after this, then I'm done. If in the 2nd example, there is Monroe NE between the barcode and the unique identifier, I use the 2nd step.
How can I delete everything before the LAST occurrence of the Underscore in one step?
This is what I tried:
1st Step :
Get-ChildItem -File "Folder Path Name" | Rename-Item -NewName { $_.Name -replace "Barcode", "" }
2nd Step:
$Files = Get-ChildItem -Path 'Folder Path Name’ -File
foreach ($File in $Files) {
$Split = $File.Name -split '_'
if ($Split.Count -gt 1) {
Rename-Item -Path $File.FullName -NewName $Split[1]}}
To get the last item in an array, use the index -1:
PS ~> $string = 'a_b_c'
PS ~> $split = $string -split '_'
PS ~> $split[-1]
c
Along with 'Mathias R. Jessen's' helpful and succinct answer; here's another option to add to your arsenal of info.
'12 44571 522110_10_E-354-98-U90368.ENG',
'14 44571 500169_9_Monroe NE_G-462-02-5674.GER' |
ForEach-Object {$PSItem.Substring($PSItem.LastIndexOf('_')+1)}
# Results
<#
E-354-98-U90368.ENG
G-462-02-5674.GER
#>

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
}

Powershell add suffix to filenames, based on prefix

I have a directory that consists of a number of text files that have been named:
1Customer.txt
2Customer.txt
...
99Customer.txt
I am trying to create powershell script that will rename the files to a more logical:
Customer1.txt
Customer2.txt
...
Customer99.txt
The prefix can be anything from 1 digit to 3 digits.
As I am new to powershell, I really don't know how I can achieve this. Any help much appreciated.
The most straigth forward way is a gci/ls/dir
with a where matching only BaseNames starting with a number with a
RegEx and piping to
Rename-Item and building the new name from submatches.
ls |? BaseName -match '^(\d+)([^0-9].*)$' |ren -new {"{0}{1}{2}" -f $matches[2],$matches[1],$_.extension}
The same code without aliases
Get-ChildItem |Where-Obect {$_.BaseName -match '^(\d+)([^0-9].*)$'} |
Rename-Item -NewName {"{0}{1}{2}" -f $matches[2],$matches[1],$_.extension}
Here is one way to do it:
Get-ChildItem .\Docs -File |
ForEach-Object {
if($_.Name -match "^(?<Number>\d+)(?<Type>\w+)\.\w+$")
{
Rename-Item -Path $_.FullName -NewName "$($matches.Type)$($matches.Number)$($_.Extension)"
}
}
The line:
$_.Name -match "^(?<Number>\d+)(?<Type>\w+)\.\w+$")
takes the file name (e.g. '23Suppliers.txt') and perform a pattern match on it, pulling out the number part (23) and the 'type' part ('Suppliers'), naming them 'Number' and 'Type' respectively. These are stored by PowerShell in its automatic variable $matches, which is used when working with regular expressions.
We then reconstruct the new file using details from the original file, such as the file's extension ($_.Extension) and the matched type ($matches.Type) and number ($matches.Number):
"$($matches.Type)$($matches.Number)$($_.Extension)"
I'm sure there's a nicer way to do this with regex, but the following is a quick first go at it:
$prefix = "Customer"
Get-ChildItem C:\folder\*$prefix.txt | Rename-Item -NewName {$prefix + ($_.Name -replace $prefix,'')}

List directory files in a order and replace using powershell

I have a remote directory under it I have a folder structure as below
Logs
Data
Install
Rollback
I know how to display from the directory in question but I need the output in following sequence sorted by lastwritetime.
\\shareddrive\xyz\Install\file1
\\shareddrive\xyz\Install\file2
\\shareddrive\xyz/Install\file3
\\shareddrive\xyz\Rollback\file1
\\shareddrive\xyz\Data\file1
\\shareddrive\xyz\Logs\file1
Now replace directory location in text file as below
..\xyz\Install\file1
..\xyz\Install\file2
..\xyz/Install\file3
..\xyz\Rollback\file1
..\xyz\Data\file1
..\xyz\Logs\file1
How can I achieve this?
I feel that you are asking for the files to be output in that exact order. To take a slightly different take on gathering the data I present the following:
$uncPath = "\\servername\xyz"
[void]($uncPath -match '^(?<Server>\\\\\w+)')
$shareServerRegex = [regex]::Escape($Matches.Server)
$shareServerReplacement = ".."
$folderOrder = "\Install\","\Rollback\","\Data\","\Logs\"
$data = Get-ChildItem $uncPath -Recurse
$folderOrder | ForEach-Object{
$currentFolder = [regex]::escape($_)
$data | Where-Object{$_.FullName -match $currentFolder} |
Sort-Object LastWriteTime |
ForEach-Object{$_.FullName -replace $shareServerRegex,$shareServerReplacement}
} | Out-File -Encoding ascii -FilePath c:\pathtofile.txt
$uncPath contains the path to the folder we are going to parse from. To make the script a little more dynamic we gather the \serverpath into $Matches.Server and the use the static method of Escape from regex to build the regex match string we will use soon to massage the path as you requested. Gather all the files from the $uncPath into the variable $data. Using the $folderOrder array we go though each item and select the files that have that as part of the folder path. You will see that the array has the folders with a slash before and after. That will ensure no files themselves, containing any of those words, end up filtered in the wrong order. Again, since we use slashes that are control characters in regex we use the Escape method to match the files from $data we need what I presume is the correct order. Proof is in the pudding.
..\xyz\Install\file1
..\xyz\Install\file2
..\xyz\Install\file3
..\xyz\Rollback\file1
..\xyz\Data\file1
..\xyz\Logs\file1
In short all I was trying to help you address is the output. You can ignore all my other code and just focus on what I think you needed help with. This will work as long as you populate $data and set or replace the parameters of -replace
$folderOrder = "\Install\","\Rollback\","\Data\","\Logs\"
...
$folderOrder | ForEach-Object{
$currentFolder = [regex]::escape($_)
$data | Where-Object{$_.FullName -match $currentFolder} |
Sort-Object LastWriteTime |
ForEach-Object{$_.FullName -replace $shareServerRegex,$shareServerReplacement}
} | Out-File -Encoding ascii -FilePath c:\pathtofile.txt

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.