rename batch of files - powershell

is there a way rename multiple items in a way that words switch place or move word to the end of the phrase/item name ?
ABCD 12550.txt into 12550 ABCD.txt
im assuming this will start with Rename-Item, but what then?
There is a space between words/phrases

Use Rename-Item with a delay-bind script block, in which you can parse and reconstruct each file name.
The following example uses the -replace operator to match and capture the base file name's first word as well as the remaining one(s), and reverses their order (effectively moves the first word to the end):
Get-ChildItem *.txt |
Rename-Item -WhatIf -NewName {
($_.BaseName -replace '^(\S+) +(.+)', '$2 $1') + $_.Extension
}
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.

Related

Remove "." in file name while retaining file extension

I am trying to use PS to rename a bunch files within a big share and one of the requirements is to remove a dot from the file name. I have tested a few things with my rather basic skills and of course the most basic of scripts zap the file extension.
I finally came up with something like this:
gci *.xlsx | rename-item -newname {$_.Name.replace(".","") + $_.extension }
But that adds the extension to the end of the filename (while keeping the file extension intact)
I thought I could zap the last four symbols using something like this:
gci *.xlsx | rename-item -newname { $_.basename.substring(0,$_.basename.length-4) + $_.extension }
Overall this seems like an overly complicated operation which could also mess up files without dots (unless I specify xlsx as only 4 symbols to be removed)
Would anyone be able to point me in the right direction to an easier solution? ;-)
You were on the right track with your second attempt: using the .BaseName and .Extension properties of the [System.IO.FileInfo] instances[1] output by Get-ChildItem allows you to modify the base name (the file name without its extension) separately, and then re-append the extension to form the full file name:
Get-ChildItem *.xlsx |
Rename-Item -NewName { ($_.BaseName -replace '\.') + $_.Extension } -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.
The above uses the regex-based -replace operator to remove all . instances from the base name; because . is a regex metacharacter (representing any single character), it must be escaped as \. in order to be used literally.
In this simple case, you could have used the [string] type's .Replace() method as well ($_.BaseName.Replace('.', '')), but -replace offers more features and has fewer surprises - see this answer for more information.
Case in point: Say you wanted to remove only the first . from the base name; -replace allows you to do that as follows (but you couldn't do it with .Replace()):
'foo.bar.baz' -replace '\.(.*)$', '$1' # -> 'foobar.baz'
[1] .BaseName isn't a native property of this type; instead, it is a property that PowerShell decorates instances of the type with, using its ETS (Extended Type System).

Replace text in files within a folder PowerShell

I have a folder that contains files like 'goodthing 2007adsdfff.pdf', 'betterthing 2007adfdsw.pdf', and 'bestthing_2007fdsfad.pdf', I want to be able to rename each, eliminating all text including 2007 OR _2007 to the end of the string keeping .pdf and getting this result: 'goodthing.pdf' 'betterthing.pdf' 'bestthing.pdf' I've tried this with the "_2007", but haven't figured out a conditional to also handle the "2007". Any advice on how to accomplish this is greatly appreciated.
Get-ChildItem 'C:Temp\' -Name -Filter *.pdf | foreach { $_.Split("_2017")[0].substring(0)}
Try the following:
Get-ChildItem 'C:\Temp' -Name -Filter *.pdf |
Rename-Item -NewName { $_.Name -replace '[_ ][^.]+' } -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.
The above uses Rename-Item with a delay-bind script block and the -replace operator as follows:
Regex [_ ][^.]+ matches everything from the first space or _ char. (character set [ _]) through to the following literal . char. ([^.]+ matches one or more chars. other than (^) than .) - that is, everything from the first / _ through to the filename extension (excluding the .).
Note: To guard against file names such as _2017.pdf matching (which would result in just .pdf as the new name), use the following regex instead: '(?<=.)[_ ][^.]+'
By not providing a replacement operand to -replace, what is matched is replace with the empty string and therefore effectively removed.
The net effect is that input files named
'goodthing 2007adsdfff.pdf', 'betterthing 2007adfdsw.pdf', 'bestthing_2007fdsfad.pdf'
are renamed to
'goodthing.pdf', 'betterthing.pdf', 'bestthing.pdf'
Without knowing the names of all the potential files, I can offer this solution that is 100%:
PS> $flist = ("goodthing 2007adsdfff.pdf","betterthing 2007adfdsw.pdf","bestthing_2007fdsfad.pdf")
PS> foreach ($f in $flist) {$nicename = ($f -replace "([\w\s]+)2007.*(\.\w+)", '$1$2') -replace "[\s_].","." ;$nicename}
goodthing.pdf
betterthing.pdf
bestthing.pdf
Two challenges:
the underscore is actually part of the \w character class. So the alternative to the above is to complicate the regex or try to assume that there will always be only one '_' before the 2007. Both seemed risky to me.
if there are spaces in filenames, there is no telling if you might encounter more than one. This solution removes only the one right before 2007.
The magic:
The -replace operator enables you to quickly capture text in () and re-use it in variables like $1$2. If you have more complex captures, you just have to figure out the order they are assigned.
Hope this helps.

Appending string to the end of all file names in PowerShell

I have files look like
data.svg
map.svg
aplicationp.svg
...
*.svg
I am trying to add -b string to the end of all files names bu using power shell rename command like
D:\icons> Dir | Rename-Item -NewName {$_.name -replace ".","-b."}
to get these
data-b.svg
map-b.svg
application-b.svg
but this is not changing anything. How can I achieve this?
Powershell's -replace operator is based on regular expressions. And since . is a wildcard in regex, what should be happening is that each character in the file name is being replaced with the resulting string. So test.txt would become -b.-b.-b.-b.-b.-b.-b in your example.
You likely want to use the Replace method of the .NET String type like this instead.
dir | Rename-Item -NewName { $_.Name.Replace('.','-b.') }
If you want to keep using -replace, you need to escape the . in your expression like this.
dir | Rename-Item -NewName { $_.Name -replace '\.','-b.' }
Both of these have a couple edge case problems that you may want to avoid. The first is narrowing the scope of your dir (which is just an alias for Get-ChildItem) to avoid including files or directories you don't actually want to rename. The second is that a simple replace in the file name doesn't account for file names that contain multiple dots. So you may want to ultimately do something like this if you only care about SVG files that may have multiple dots.
Get-ChildItem *.svg -File | Rename-Item -NewName { "$($_.BaseName)-b$($_.Extension)" }
The replace operator uses regex. Therefore your . needs to be escaped, otherwise it just stands for any character. I would generally make sure to be as specific as possible when writing regexes. The following is a possible solution
Get-ChildItem *.svg | Rename-Item -NewName { $_.name -Replace '\.svg$','-c.svg' }
The $ anchors the expression to the end of the string which makes sure it only changes the extension and not any other text inside the file names.

Using Powershell to reverse the sequence numbers in a set of file names

I am new to using PowerShell (and coding for that matter), all I'm trying to do is rename my files).
I have 222 .jpg's that are named
"REF_10001.jpg"
"REF_10002.jpg"
"REF_10003.jpg"
etc
but my problem is that they are in the wrong order, I need to reverse them, for example I need
"REF_10001.jpg" --> "REF_10222.jpg"
and vice versa.
"REF_10222.jpg" --> "REF_10001.jpg"
Is there a way to do this? I have been struggling for hours to rename my files properly, just to realize they are in the wrong order, I am tempted to just go do them individually at this point.
Also if someone could help me to change the files to read
"REF.0001.jpg"
"REF.0002.jpg"
etc.
that would be great.
Or if what I'm asking isn't possible, I have a back up folder of my image sequence before i started trying to rename them, the files read
_0221_Layer 1.jpg
_0220_Layer 2.jpg
...
_0000_Layer 222.jpg
I need to change them so that
"_0221_Layer 1.jpg" --> "Ref.0001.jpg"
...
"_0000_Layer 222.jpg" --> "Ref.0222.jpg"
Try this:
Get-ChildItem ".\Pics\*.jpg" |
ForEach-Object {$i = 1} {
Rename-Item -LiteralPath $_.FullName -NewName "$($_.BaseName)-$i.jpg"
$i++
}
Get-ChildItem ".\Pics\*.jpg" |
ForEach-Object {$j = 222} {
Rename-Item -LiteralPath $_.FullName -NewName "REF_$("1{0:0000}" -f $j).jpg"
$j--
}
It's seems a bit inefficient to me, in that you need to enumerate twice, but it works on my test files, so will hopefully be good enough to resolve your immediate problem.
The first loop adds a unique 'tag' to the filename that is later discarded - this is needed otherwise you end up with clashes (e.g. if you try to name "REF_10001" to "REF_10222", it will fail since the latter already exists.
My assumptions:
We do not know in advance if the numbers in the file names always follow each other
We do not know in advance the number of digits in these numbers
I would only take files starting REF_ followed by numbers and ending with the extension jpg
So I propose a solution that reverses the first name with the last one, the second with the last before and so on :
$DirWithFiles="C:\Temp\DirWithFilesToRename"
#Creation to new dir for copy
$Temporatydir= [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
New-Item -ItemType Directory $Temporatydir
#get all file jgp with format REF_ + Number + .jpg and order by number
$ListFile=Get-ChildItem $DirWithFiles -File -Filter "REF_*.jpg" | where BaseName -Match "^REF_\d{1,}$" | select Fullname, Name, #{N="Number";E={[int]($_.BaseName -split '_')[1]}} | sort Number
#copy files into new temporary dir and rename 1 position by N position, 2 by N-1, 3 by N - 2 etc...
for ($i = 0; $i -lt ($ListFile.Count / 2 ); $i++)
{
Copy-Item $ListFile[$i].FullName "$Temporatydir\$($ListFile[$ListFile.Count-$i - 1].Name)" -Force
Copy-Item $ListFile[$ListFile.Count-$i - 1].FullName "$Temporatydir\$($ListFile[$i].Name)" -Force
}
#Copy all files in temporary dir to initial dir with overwriting
Get-ChildItem $Temporatydir | Copy-Item -Destination $DirWithFiles
#delete temporary dir
Remove-Item $Temporatydir -Recurse
A simple method to reverse the order is:
subtract the highest number+1 from each file number.
to remove the resulting negative sign multiply with -1 or use the [math]::abs() function.
To get the number from the (Base)Name a Regular Expression '^REF_1(\d+)?' captures () the number without the leading 1.
a format string operator "REF.{0:0000}.jpg" -f is used to create the name inserting the calculated new number with 4 places
As the prefix changes there is danger overwriting a file from the other end.
Get-ChildItem REF_1*.jpg |
Where-Object BaseName -match '^REF_1(\d+)?' |
Rename-Item -NewName {"REF.{0:0000}.jpg" -f (-1 *($Matches[1]-223))} -whatif
If the output looks OK remove the trailing -WhatIf parameter.
Sample output last line:
What if: Performing the operation "Rename File" on target "Item: REF_10222.jpg
Destination: REF.0001.jpg".
The following works with a variable number of files and also performs the desired name transformation (in addition to reversing the sequence numbers):
# Enumerate the files in reverse alphabetical order - which thanks to the 0-padding
# is equivalent to the reverse sequence-number order - and rename them with
# an ascending 0-padded sequence number.
# Note that in-place renaming is only possible because the new filename pattern
# - REF.*.jpg - doesn't conflict with the old one, REF_1*.jpg
$iref = [ref] 0 # sequence-number helper variable
Get-ChildItem -Filter REF_1*.jpg | Sort-Object Name -Descending |
Rename-Item -NewName { 'REF.{0:0000}.jpg' -f (++$iref.Value) } -WhatIf
Note: The use of -WhatIf previews the command without actually running it.
If the preview shows the intended operations, remove -WhatIf to actually perform it.
Note the need to use a [ref]-typed aux. variable for the sequence number and to increment it via its .Value property in the script block ({ ... }) that calculates the new name, because the script block runs in a different variable scope and therefore cannot directly modify variables in the caller's scope.

Rename-Item renaming issue

I am wrote some code and the first time it worked fine. However, I've now replaced the files with new dummy files and I'm now getting the following:
MyTestFile.zip is changed to MyTestFile_zip.zip_zip.zip
MyTestFile.prd is changed to MyTestFile_prd.prd_prd.prd
Here's the code:
Get-ChildItem -Filter "*.prd" -Recurse | Rename-Item -NewName {$_.name -replace '.prd','_prd.prd'}
Get-ChildItem -Filter "*.zip" -Recurse | Rename-Item -NewName {$_.name -replace '.zip','_zip.zip'}
Got any ideas how I can avoid this problem?
Thanks
The -replace operator
takes a regular expression as its first operand and
performs substring matching by default.
Therefore, you must modify the regex that is the first operand for the replacement to work as intended:
$_.Name -replace '\.prd$','_prd.prd'
. is escaped as \. to make sure that -replace treats it as a literal; without the \, . would match any character.
$ ensures that the expression is anchored to the end of the input, i.e., only matches at the end.
A more generic reformulation that covers both .prd and .zip:
$_.Name -replace '\.(prd|zip)$','_$1.$1'
See below for an explanation.
If we put this all together, we can get away with a single pipeline:
Get-ChildItem -File *.prd, *.zip -Recurse |
Rename-Item -NewName { $_.Name -replace '\.(prd|zip)$','_$1.$1' }
Use of regular expressions allows you to process more than one extension at a time, and also allow you to reference parts of what the regular expression matched (captured) through capture groups (parenthesized subexpressions) that you can reference as $1 (1st capture group, ...) in the replacement string
> #( 'example.zip', 'example.prd' ) -replace '\.(prd|zip)$','_$1.$1'
example_zip.zip
example_prd.prd
Note how $1 in the replacement string refers to whatever capture group (prd|zip) captured, which is zip for the first input, and prd for the second.
Editor's note:
While this answer doesn't work for the specific question at hand, because it inserts an extraneous ., it does show the use of [System.IO.Path]::ChangeExtension() as a robust way to change a file's extension in general.
Alternatively use the .net method to change the file extension:
Get-ChildItem -Filter "*.prd" -Recurse | Rename-Item -NewName {[System.IO.Path]::ChangeExtension($_.name,"_prd.prd")}