I have files that look like this:
2020-0517-example.pdf
2020-0412-example.pdf
2020-0607-example.pdf
I would like to rename the files to:
2020-0723-example.pdf
2020-0723-example.pdf
2020-0723-example.pdf
I would basically be replacing the dates to today's date while keeping the year and the suffix.
If you want to add a bit of sanity checking to make sure you are dealing with dates, you can use pattern matching and then rename the partial date:
$date = Get-Date -Format MMdd
Get-ChildItem -Path <filepath> -File |
Where Basename -match '^(?:19|20)\d\d-(?:0[1-9]|1[012])\d\d-' |
Rename-Item -NewName { $_.Name -replace '(?<=^\d{4}-)\d{4}',$date } -whatif
Alternatively, you can parse the date string first to verify it using TryParseExact:
$date = Get-Date -Format MMdd
Get-ChildItem -Path <filepath> -File |
Where Name -match '^\d{4}-\d{4}-' | Foreach-Object {
if ([datetime]::TryParseExact($_.Name.Substring(0,9),'yyyy-MMdd',[System.Globalization.CultureInfo]::InvariantCulture,[System.Globalization.DateTimeStyles]::None,[ref]0)) {
$FileParts = $_.Name -split '-',3
Rename-Item -Path $_ -NewName ($FileParts[0],$date,$FileParts[2] -join '-') -WhatIf
}
}
You will need to remove the -WhatIf parameter for the rename operation to complete.
Explanation:
Using -split '-',3, we ensure that we are left with no more than three array elements. This provides a predictable number of indexes for putting the file name back together. [0] will be the year. [1] will be the month-day. [2] will be the remainder of the file name.
Please see the following for the -replace and -match regex details:
Regex -Match
Regex -Replace
You could split by "-" and stitch together with String.Format:
$filename = "2020-0517-example.pdf"
$today = Get-Date
$arr = $filename.Split("-")
$newname = "{0}-{1:MMdd}-{2}" -f $arr[0], $today, $arr[2]
Related
I got a problem with powershell and some pdfs names
So there is a bunch of them:
2021_01_21_Aneks_nr2.pdf
2021_11_31_Aneks_nr3.pdf
2021_05_04_Aneks_nr4.pdf
I need to replace "_" with "-" but only near date.
Output should look like this:
2021-05-04_Aneks_nr4.pdf
I'm new to programming and couldn't find solution in internet.
I was using this code :
Get-Childitem | foreach { rename item $_ $_.Name.Replace("_","-") }
But it works on all "_" so I cant use it in this scenario.
Thanks for help! :)
Using the -replace operator instead of the dot net method you can use regex. And there you can specify to replace only underlines between digits:
Get-ChildItem |
ForEach-Object {
Rename-Item -Path $_.FullName -NewName $($_.Name -replace '(?<=\d)_(?=\d)', '-')
}
Here you go
#Loop through files
get-childitem [path] | ForEach-Object {
#match pattern
$null = $_.name -match '\d{4}_\d{2}_\d{2}'
#get matching pattern
$pattern = $matches[0]
#replace _ with -
$replace = $pattern -replace '_','-'
#build new name
$newName = $_.name -replace $pattern,$replace
#rename file
rename-item -Path $_.fullname -NewName $newName
}
It matches the current name of the file for the pattern '\d{4}_\d{2}_\d{2}', gets the matching string, replaces _ with - and finally build the new name by replacing the matching string with the updated version.
I've got a set of files in the following format
I'd like to change the file names to the following format
I've used the following code:
`$i = 21
dir | ForEach-Object {$_ | Rename-Item -NewName ('00816-101998-XX-A-2-50-{0:D3} Detalj.{1}' -f $i++, $_.Extension)}`
This code starts renaming with file name A.10.11.11
How do I get it to start from the first file name i.e. 8.A.10.11.8?
Thank you!
Looking at your question, I think this is what you intend to do.
The list of current filenames should be sorted by the integer value the filenames start with.
Next you want to rename them using a counter that starts with value 21
To do this, you can use a ForEach-Object loop, but the -NewName parameter of Rename-Item can also contain a scriptblock containing the action to perform.
$i = 21
(Get-ChildItem -Path 'D:\Test' -Filter '*.pdf' -File) |
Sort-Object #{Expression = {[int]($_.Name -split ' ')[0]}} |
Rename-Item -NewName { '00816-101998-XX-A-2-50-{0:D3} Detalj{1}' -f $script:i++, $_.Extension }
P.S. The image shows the filename starts with a number followed by a space, while in the question you give an example where the number is followed by a dot .. If that is the case in you file names, change [int]($_.Name -split ' ')[0] into [int]($_.Name -split '\.')[0]
1. We need to address and increment the value of the counter $i using the $script:i scoping syntax, otherwise $i does not exist in the NewName scriptblock
2. To avoid renaming files twice, enclose the Get-ChildItem part in the code in between brackets
param(
$directory = "d:\tmp",
[regex]$rx = "(?<=8.A.10.11.)\d+",
[string]$newPrefix = "00816-101998-XX-A-2-50-"
)
<#
8..20 | ForEach-Object {
$filePath = Join-Path $directory -ChildPath "8.A.10.11.$_.pdf"
Out-File -InputObject "some text" -FilePath $filePath -Encoding default
}
#>
Get-ChildItem $directory | ForEach-Object{
Rename-Item $_.FullName -NewName "$newPrefix$((($rx.Matches($_.BaseName)).value).padleft(3,'0'))$($_.Extension)"
}
I am using below Powershell script which successfully traverses through all my case folders within the main folder named Test. What it is incapable of doing is to rename each sub folder, if required, as can be seen in current and desired output. Script should first sort the sub folders based on current numbering and then give them proper serial numbers as folder name prefix by replacing undesired serial numbers.
I have hundreds of such cases and their sub folders which need to be renamed properly.
The below output shows two folders named "352" and "451" (take them as order IDs for now) and each of these folders have some sub-folders with a 2 digit prefix in their names. But as you can notice they are not properly serialized.
$Search = Get-ChildItem -Path "C:\Users\User\Desktop\test" -Filter "??-*" -Recurse -Directory | Select-Object -ExpandProperty FullName
$Search | Set-Content -Path 'C:\Users\User\Desktop\result.txt'
Below is my current output:
C:\Users\User\Desktop\test\Case-352\02-Proceedings
C:\Users\User\Desktop\test\Case-352\09-Corporate
C:\Users\User\Desktop\test\Case-352\18-Notices
C:\Users\User\Desktop\test\Case-451\01-Contract
C:\Users\User\Desktop\test\Case-451\03-Application
C:\Users\User\Desktop\test\Case-451\09-Case Study
C:\Users\User\Desktop\test\Case-451\14-Violations
C:\Users\User\Desktop\test\Case-451\21-Verdict
My desired output is as follows:
C:\Users\User\Desktop\test\Case-352\01-Proceedings
C:\Users\User\Desktop\test\Case-352\02-Corporate
C:\Users\User\Desktop\test\Case-352\03-Notices
C:\Users\User\Desktop\test\Case-451\01-Contract
C:\Users\User\Desktop\test\Case-451\02-Application
C:\Users\User\Desktop\test\Case-451\03-Case Study
C:\Users\User\Desktop\test\Case-451\04-Violations
C:\Users\User\Desktop\test\Case-451\05-Verdict
Thank you so much. If my desired functionality can be extended to this script, it will be of great help.
Syed
You can do the following based on what you have posted:
$CurrentParent = $null
$Search = Get-ChildItem -Path "C:\Users\User\Desktop\test" -Filter '??-*' -Recurse -Directory | Where Name -match '^\d\d-\D' | Foreach-Object {
if ($_.Parent.Name -eq $CurrentParent) {
$Increment++
} else {
$CurrentParent = $_.Parent.Name
$Increment = 1
}
$CurrentNumber = "{0:d2}" -f $Increment
Join-Path $_.Parent.FullName ($_.Name -replace '^\d\d',$CurrentNumber)
}
$Search | Set-Content -Path 'C:\Users\User\Desktop\result.txt'
I added Where to filter more granularly beyond what -Filter allows.
-match and -replace both use regex to perform the matching. \d is a digit. \D is a non-digit. ^ matches the position at the beginning of the string.
The string format operator -f is used to maintain the 2-digit requirement. If you happen to reach 3-digit numbers, then 3 digit numbers will be output instead.
You can take this further to perform a rename operation:
$CurrentParent = $null
Get-ChildItem . -Filter '??-*' -Recurse -Directory | Where Name -match '^\d\d-\D' | Foreach-Object {
if ($_.Parent.Name -eq $CurrentParent) {
$Increment++
} else {
$CurrentParent = $_.Parent.Name
$Increment = 1
}
$CurrentNumber = "{0:d2}" -f $Increment
$NewName = $_.Name -replace '^\d\d',$CurrentNumber
$_ | Where Name -ne $NewName | Rename-Item -NewName $NewName -WhatIf
}
$NewName is used to simply check if the new name already exists. If it does, a rename will not happen for that object. Remove the -WhatIf if you are happy with the results.
I know this is a new guy question, but I cant find anything. I think the issue might have to do with variable type issue?
I'm trying to look at a files name and see if pattern1 is contained in the file name. If so then replace the pattern text with "TEST".
Right now it doesn't error out, but it skips the IF, I do have files with the pattern in the directory.
Can't insert actually code, so here is a sample
$pattern1 = "January"
$pattern2 = "December 31"
$path = "C:\Users\...\METRICS-TEST\Metrics 2014\January 2014 Client Metrics"
$search_results = Get-ChildItem -Path $path | Where-Object { ((! $_.PSIsContainer))}
foreach ($file in $search_results) {
if($file.Name -contains $pattern1){
$new_name = $file.Name -replace $pattern1, "TEST1"
Rename-Item -Path $file.FullName -NewName $new_name
}else{
$new_name = $file.Name -replace $pattern2, "TEST2"
Rename-Item -Path $file.FullName -NewName $new_name
}
}
-contains work on collections, not strings.
For strings, you'd want either -match (for RegEx matching) or -like for simple wildcard matching:
$file.name -match $pattern1
$file.name -like "*$pattern1*"
-contains would be appropriate if you had an array of strings and wanted to know if it contained one or more copies of a specific string:
$Strings = "abc","def","ghi","jkl"
# This evaluates to true
$Strings -contains "abc"
# This evaluates to false
$Strings -contains "ab"
The whole issue is that you use -Contains where you should use -Match. Try this though, it's easily expandable if you have more conditions to add:
$pattern1 = "January"
$pattern2 = "December 31"
$path = "C:\Users\...\METRICS-TEST\Metrics 2014\January 2014 Client Metrics"
Switch(Get-ChildItem -Path $path){
{$_.Name -match $pattern1}{$new_name = $_.Name -replace $pattern1, "TEST1"
Rename-Item -Path $_.FullName -NewName $new_name}
{$_.Name -match $pattern2}{$new_name = $_.Name -replace $pattern2, "TEST2"
Rename-Item -Path $_.FullName -NewName $new_name}
}
I realize I have an accepted answer, but I thought another elegant way to handle this would be mapping Old-To-New in a hashtable, matching against the keys of that hashtable (regex escaped and joined with pipes to form the regex match pattern), and then renaming in a ForEach against the matches rather than a switch. I suppose at this point it's all academic.
$path = "C:\Users\...\METRICS-TEST\Metrics 2014\January 2014 Client Metrics"
$RenameMap = #{
"January" = "Test1"
"December 31" = "Test2"
}
$Pattern = "($(($RenameMap.keys|ForEach{[regex]::Escape($_)}) -join "|"))"
Get-ChildItem $Path | Where{$_.Name -match $Pattern} | ForEach {Rename-Item $_.FullName -NewName ($_.Name -Replace $Matches[1],$RenameMap[$Matches[1]])}
How do you trim the date from a text file. For example, I have multiple files like:
test_20091011.txt
try_20091011.txt
fold_20091011.txt
I would like to change into:
test.txt
try.txt
fold.txt
Thanks.
This code should work assuming the text file name format doesn't change, as in, it's always the last 9 characters you need to remove. This code assumes the txt files are in the folder C:\folder
$filelist = (get-childitem c:\folder | Where-Object {$_.mode -match "a"} | foreach-object {$_.name})
foreach ($file in $filelist)
{
$len = $file.length
$newname = $file.substring(0,$len -13)
$newname = $newname + '.txt'
Rename-Item C:\folder\$file $newname
clear-variable newname, len
}
The answer for this will change subtly depending on the type of naming pattern, but in your case, you can accomplish this with a script like this:
Get-ChildItem |
Where-Object {
<#
Multiple Assignment in PowerShell.
$beforeUnderbar will have your name,
$AfterUnderBar will have the data, and
$extension will have the extension.
All from one little -split
If you start throwing random other files in there, it will barf.
#>
$beforeUnderbar, $afterUnderBar, $extension = ($_.Name -split "[_.]")
if ($afterUnderBar -and $afterUnderBar.Length -eq 8 -and $afterUnderBar -as [int]) {
"$beforeUnderBar.$extension"
}
}
That script should give you what you want from files that match your naming convention.