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)"
}
Related
My PowerShell script creates a log file, but when I run the script for the second time, it tells me that the testfile1.log file already exists.
How do I make the script if it finds testfile1.log, it creates testfile2.log, and if this also exists, it creates testfile3.log, and so on..
New-Item -Path $path -Name "testfile1.log" -ItemType "file"
You could do it this way, first get all the files in the desired path and sort them by the ending digits on their name. If no files are found create the testfile1.log, if there were files found, get the last sorted file (the one with the highest ending digit) extract the ending digits and add +1 to the count and use it to create the new file.
$files = Get-ChildItem $path -Filter testfile*.log | Sort-Object {
$_.BaseName -replace '\D' -as [int]
}
if(-not $files)
{
New-Item -Path $path -Name "testfile1.log" -ItemType File
}
else
{
[int]$number = $files[-1].BaseName -replace '\D'
$number++
New-Item -Path $path -Name "testfile$number.log" -ItemType File
}
An alternative method, based on this answer could be
$path = 'D:\Test'
$log = 'testfile'
$index = ((Get-ChildItem -Path $path -Filter "$log*.log" -File |
Where-Object { $_.BaseName -match "$log\d+$" } |
Select-Object #{Name = 'index'; Expression = {[int]($_.BaseName -replace '\D')}}).index |
Measure-Object -Maximum).Maximum + 1
# create the new file
New-Item -Path (Join-Path -Path $path -ChildPath "$log${index}.log") -ItemType File
A concise solution that also builds on this answer (see there for an explanation of the core technique):
$path = '.' # Output dir.
$nameTemplate = 'testfile{0}.log' # {0} is the sequence-number placeholder
New-Item -ItemType File -Path $path -Name (
$nameTemplate -f (1 + (
# Find all existing log files
Get-ChildItem (Join-Path $path $nameTemplate.Replace('{0}', '*')) |
Measure-Object -Maximum {
# Extract the embedded sequence number.
$_.Name -replace [regex]::Escape($nameTemplate).Replace('\{0}', '(\d+)'), '$1'
}
).Maximum)
) -WhatIf
Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
Note:
The above uses a complex -replace operation to reliably extract the sequence number from existing file names; if you know that that only one number is present in each given file name, $_.BaseName -replace '\D' (removing all non-digit characters) will do in the Measure-Object call above.
If you wanted to use zero-padded, fixed-width sequence numbers, you can adjust (all occurrences of) the {0} placeholder accordingly; e.g, to create sequence numbers 01, 02, ... 99, use {0:00} - see the Composite formatting help topic, which describes the string formatting language also used by PowerShell's -foperator
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 need to bulk rename files in a file share that
contain a specific character, namely a tilde ~ and
have the file extension in capital letters or none at all.
The goal would be to replace the tilde with a simple -, keep the file extension, if there is one, but transform it into lowercase letters.
I've had success with the first part of the script that finds the files
$PATH = "\\<Fileservername>\<Folder>\"
$pattern = "*~*"
Get-ChildItem $PATH -Recurse | where {$_.Name -like $pattern}
What I'm struggling with is the second part of the script the renaming.
I've found two topics here:
Powershell renaming a specific Character
PowerShell Regex Bulk Replace Filenames
I haven't been able to adapt those solutions to my need plus there may be additional steps to take in order to convert the given file name from capital letters to lowercase letters or skip this if the file has no file extension.
An example would be to rename ACUJLH~H to ACUJLH-H and KYA3BM~Q.PDF to KYA3BM-Q.pdf.
Here's my contribution. I have added the -File switch to the Get-ChildItem cmdlet so it will look for files only and will not try and handle directory names.
Also, I have changed the replace pattern to ~+ so files that have repeating tildes will be replaced with a single - character. (KYA3BM~~~~Q.PDF becomes KYA3BM-Q.pdf)
$path = "D:\Code\PowerShell\StackOverflow"
$pattern = "*~*"
Get-ChildItem $path -Recurse -File | Where-Object {$_.Name -like $pattern} |
ForEach-Object {
$directory = $_.DirectoryName # or [System.IO.Path]::GetDirectoryName($_.FullName) or use Split-Path $_.FullName -Parent
$filename = $_.BaseName -replace '~+', '-' # or [System.IO.Path]::GetFileNameWithoutExtension($_.Name) -replace '~+', '-'
$extension = $_.Extension # or [System.IO.Path]::GetExtension($_.Name)
if (![string]::IsNullOrEmpty($extension)) { $filename += $extension.ToLower() }
$newname = Join-Path -Path $directory -ChildPath $filename
Rename-Item -LiteralPath $_.FullName -NewName $newName -Force
}
You will need to filter files those meets your criteria. Then using ForEach-Object compare for extensions and build new file names for every found item. Finally, using Rename-Item cmdlet you make the change.
$PATH = '\\<Fileservername>\<Folder>\'
Get-ChildItem $PATH -Recurse -Include '*~*' | ForEach-Object {
[String]$Extension = [System.IO.Path]::GetExtension($_)
[String]$NewFileName = [System.IO.Path]::GetFileNameWithoutExtension($_.Name) -replace '~','-'
if ($Extension){ $NewFileName += $Extension.ToLower() }
Rename-Item $_.FullName $(Join-Path ([System.IO.Path]::GetDirectoryName($_)) $NewFileName) -Force
}
I'm new to using PowerShell and am wondering how to simply pad number strings contained in several similar file names to have the same number of digits in each file name.
That is, I have a folder containing these 300 files:
dummy name 1.txt through dummy name 300.txt
and would simply like rename the files that have less than 3 digits in them to all have exactly 3 digits, such as:
dummy name 001.txt through dummy name 300.txt
If your files were produced sequentially and you've got name1.txt
, name2.txt, etc.,
And you want name0001.txt, name0002.txt, etc.
You can do
$j=1;
foreach ($i in Get-ChildItem "./name?.txt" -Name) {
echo $i;
$new_name = "name000" + $j + ".txt";
Rename-Item -Path $i -NewName $new_name;
$j=$j+1;
}
... Then do it again, changing $j to 10 at the start, taking a 0 off, and so on for the hundreds.
Okay for a one off and if you can then fix what's producing the files in the first place.
#TessellatingHeckler gave good answer in the comments:
gci | ren -n {[regex]::replace($_.name, '\d+', {"$args".PadLeft(3, '0')})}
I would do it like this:
$i=0;
$nameLike='dummy name'
[int]$currentid=0
#RENAME TO REMOVE THE SPACE IN THE MIDDLE
gci -File -Path "./" | where{ $_.name -like "$nameLike*"} | %{
#remove space in the middle
$newname = $_.Name.Split(' ')[0] + $_.Name.Split(' ')[1]
#convert the name into the padding you want (3)
[int]::TryParse($_.Name.Split(' ')[2].Split('.')[0],[ref]$CurrentId) | Out-Null
$newname = $newname + $CurrentId.ToString("000") + $_.Extension
Rename-Item -Path $_.FullName -NewName $newname
}
Details in here:
https://medium.com/#josegabrielortegac/powershell-renaming-files-with-powershell-e9012573647a?sk=502148e039058d84fe34608d77cd1aa2
Try this:
1..300 | %{
$source = "dummy name $_.txt"
$target = ('dummy name {0:00#}.txt' -f $_)
rename-item $source -NewName $target
}
Notes:
The outer loop is a little unconventional. Most scripters code this to resemble a java for loop.
The choice of single and double quotes is intentional. Be careful.
The code is a little inelegant, because some parts of the filename are typed in twice.
The assumption is that all 300 files exist. If not, you will get errors.
try this:
Get-ChildItem "c:\temp\" -file -Filter "*.txt" | where basename -match ". \d{1,2}$" | %{
$res=$_.BaseName -split '(.)(\d{1,2})$'
$newnamme="{0}\{1} {2:D3}{3}" -f $_.Directory, $res[0], [int]$res[2], $_.Extension
Rename-Item $_.FullName $newnamme
}
How can I change the following code to look at all the .log files in the directory and not just the one file?
I need to loop through all the files and delete all lines that do not contain "step4" or "step9". Currently this will create a new file, but I'm not sure how to use the for each loop here (newbie).
The actual files are named like this: 2013 09 03 00_01_29.log. I'd like the output files to either overwrite them, or to have the SAME name, appended with "out".
$In = "C:\Users\gerhardl\Documents\My Received Files\Test_In.log"
$Out = "C:\Users\gerhardl\Documents\My Received Files\Test_Out.log"
$Files = "C:\Users\gerhardl\Documents\My Received Files\"
Get-Content $In | Where-Object {$_ -match 'step4' -or $_ -match 'step9'} | `
Set-Content $Out
Give this a try:
Get-ChildItem "C:\Users\gerhardl\Documents\My Received Files" -Filter *.log |
Foreach-Object {
$content = Get-Content $_.FullName
#filter and save content to the original file
$content | Where-Object {$_ -match 'step[49]'} | Set-Content $_.FullName
#filter and save content to a new file
$content | Where-Object {$_ -match 'step[49]'} | Set-Content ($_.BaseName + '_out.log')
}
To get the content of a directory you can use
$files = Get-ChildItem "C:\Users\gerhardl\Documents\My Received Files\"
Then you can loop over this variable as well:
for ($i=0; $i -lt $files.Count; $i++) {
$outfile = $files[$i].FullName + "out"
Get-Content $files[$i].FullName | Where-Object { ($_ -match 'step4' -or $_ -match 'step9') } | Set-Content $outfile
}
An even easier way to put this is the foreach loop (thanks to #Soapy and #MarkSchultheiss):
foreach ($f in $files){
$outfile = $f.FullName + "out"
Get-Content $f.FullName | Where-Object { ($_ -match 'step4' -or $_ -match 'step9') } | Set-Content $outfile
}
If you need to loop inside a directory recursively for a particular kind of file, use the below command, which filters all the files of doc file type
$fileNames = Get-ChildItem -Path $scriptPath -Recurse -Include *.doc
If you need to do the filteration on multiple types, use the below command.
$fileNames = Get-ChildItem -Path $scriptPath -Recurse -Include *.doc,*.pdf
Now $fileNames variable act as an array from which you can loop and apply your business logic.
Other answers are great, I just want to add... a different approach usable in PowerShell:
Install GNUWin32 utils and use grep to view the lines / redirect the output to file http://gnuwin32.sourceforge.net/
This overwrites the new file every time:
grep "step[49]" logIn.log > logOut.log
This appends the log output, in case you overwrite the logIn file and want to keep the data:
grep "step[49]" logIn.log >> logOut.log
Note: to be able to use GNUWin32 utils globally you have to add the bin folder to your system path.