Rename multiple files with special characters in filenames - powershell

Trying to remove the first few characters from a large number of files in the same directory, but having trouble with special characters in the filenames. I also need to use a wildcard to specify what text to remove since that text may contain a one or two digit number.
I'm a novice using v2.0.1.1.
an example filename is
(2) S17- 22429 E1_[49946,12147]_composite_image.jpg
I need to remove the initial number in parenthesis along with the following space. the number in parenthesis may be one or two digits, hence the need for a wildcard.
Resulting filename should be:
S17- 22429 E1_[49946,12147]_composite_image.jpg
All files will contain coordinates separated by "," in square brackets, and all will be .jpg, but everything else in the final names names will vary.
Been reading through various articles on bits and pieces of what is going wrong, have tried code below.
Issue seems to be with square brackets and maybe the comma, but only techniques I can find (``[, etc.) tell you how to deal with them in the search string, not in the portion of the string you are uninterested in. -literalpath does not seem to help, either.
dir | rename-item -NewName { ($_.name) -Replace("\(*\) ","")}
And have tried various alterations to this basic code.
Runs through every file, but all with:
Rename-Item : Cannot rename because item at 'Microsoft.PowerShell.Core\FileSystem::C:\...\(2) S17- 22429 E1_[49946,12147]_composite_image.jpg' does not exist.
At line:1 char:19
+ dir | rename-item <<<< -NewName { ($_.name) -Replace("\(*\) ","")}
+ CategoryInfo : InvalidOperation: (:) [Rename-Item], PSInvalidOperationException
+ FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.RenameItemCommand

No need for powershell complex scripts, you can do in simple CMD.
This will rename files with 1 length number:
for %a in ("(?) *.*") do ren "%a" "////*.*"
And this for a 2 length number:
for %a in ("(??) *.*") do ren "%a" "/////*.*"
you might be tempted to do:
for %a in ("(*) *.*") do ren "%a" "////*.*"
but then you will end up filenames with spaces at the beginning if they have 2+ length numbers.
How it works ?
Let's first see the rename:
So if you have this filename: (2) S17- 22429 E1_[49946,12147]_composite_image.jpg, what we want to do is:
ren "(?) S17- 22429 E1_[49946,12147]_composite_image.jpg" "////*.*";
The ? question mark means any one length character.
The * asterisk means any many characters.
The / means discard this one character.
so (2) S17- 22429 E1_[49946,12147]_composite_image.jpg => S17- 22429 E1_[49946,12147]_composite_image.jpg
for the 2 length numbers, you will need two ?? and five ///// like this:
ren "(??) S17- 22429 E1_[49946,12147]_composite_image.jpg" "/////*.*";
We use the space after the ) so the pattern is matched more strict.
Then we use:
for %a in (`condition`) do `command`
it iterates for every file in condition and then execute the command. %a is a special wildcard that you can use on the command to get the filename.
so basically, what we do is for each file, rename discarding the first 4 or 5 charaters, and we use the ? in the condition so we get all files that matches the pattern.
and voila, you can also adapt as your needs.

Looks like the Rename-Item has problems with the special characters indeed. When Cmdlets fail you it is often a good idea to look for object methods. Try this one. It works for me on PS 5.1
gci *.jpg | % {
$_.MoveTo("$($_.DirectoryName)\$($_.Name -replace '\(.*\) ','')")
}

Works for me if I do:
Get-ChildItem -Path 'D:\' -Filter '*.jpg' |
ForEach-Object { $_ | Rename-Item -NewName ($_.Name -replace '^\(\d+\)\s*', '')}
This is assuming the filename always starts with a number between the brackets. If it can be any character (or even nothing) between these brackets, do ($_.Name -replace '^\(.*\)\s*', '')
EDIT
Apparently, renaming multiple files through the pipeline needs the syntax for Rename-Item to be a script block, as shown in the fourth example in the docs.
If you do this:
Get-ChildItem -Path 'D:\' -Filter '*.jpg' | Rename-Item -NewName { $_.Name -replace '^\(\d+\)\s*', '' }
It works.
So, you almost got it right except for two things:
you've added round brackets to the -replace parameter
you've added the regex quantifier * directly after the opening bracket, so basically your regex asked for zero or more ( characters instead of whatever is between the brackets in the file name
Hope that helps

Related

rename batch of files

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.

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.

How to bulk rename files in folder such that all characters BEFORE and including "_" is removed

I've been searching online for some help on this but can't seem to find the right answer.
Everything I've come across so far helps with renaming files in batch, but only such that the files are renamed by trimming all characters AFTER a special character (in my case it's "_"). I would actually like to know how to rename all files in a folder such that I trim all characters BEFORE (and including) the underscore.
Example: I have "AB CD_2019481-1" and want the name to be "2019481-1"
I would be open to using Powershell or CMD!
Thanks in advance for any help.
If you know that there is one and only one underscore in all of the file names, you can do a -split on the underscore character, then take the right side of the split.
$Filename = 'AB CD_2019481-1'
$NewFilename = ($Filename -split '_')[1]
The -split '_' splits the string into an array based on the delimiter, underscore. Then the [1] retrieves the 2nd element from the left, which should be the right-hand side of the filename.
Try this out. With the -whatif, it's harmless. It should do what you ask. If your filename has more than one underscore, it may not do what you want. You can pipe get-item or get-childitem to it.
get-item 'AB CD_2019481-1' |
rename-item -newname { $_ -replace '.*_' } -whatif

Rename files with Powershell if file has certain structure

I am trying to rename files in multiple folder with same name structure. I got the following files:
(1).txt
(2).txt
(3).txt
I want to add the following text in front of it: "Subject is missing"
I only want to rename these files all other should remain the same
Tip of the hat to LotPings for suggesting the use of a look-ahead assertion in the regex.
Get-ChildItem -File | Rename-Item -NewName {
$_.Name -replace '^(?=\(\d+\)\.)', 'Subject is missing '
} -WhatIf
-WhatIf previews the renaming operation; remove it to perform actual renaming.
Get-ChildItem -File enumerates files only, but without a name filter - while you could try to apply a wildcard-based filter up front - e.g., -Filter '([0-9]).*' - you couldn't ensure that multi-digit names (e.g., (13).txt) are properly matched.
You can, however, pre-filter the results, with -Filter '(*).*'
The Rename-Item call uses a delay-bind script block to derive the new name.
It takes advantage of the fact that (a) -rename returns the input string unmodified if the regex doesn't match, (b) Rename-Item does nothing if the new filename is the same as the old.
In the regex passed to -replace, the positive look-ahead assertion (?=...) (which is matched at the start of the input string (^)) looks for a match for subexpression \(\d+\)\. without considering what it matches a part of what should be replaced. In effect, only the start position (^) of an input string is matched and "replaced".
Subexpression \(\d+\)\. matches a literal ( (escaped as \(), followed by 1 or more (+) digits (\d), followed by a literal ) and a literal . (\.), which marks the start of the filename extension. (Replace .\ with $, the end-of-input assertion if you want to match filenames that have no extension).
Therefore, replacement operand 'Subject is missing ' is effectively prepended to the input string so that, e.g., (1).txt returns Subject is missing (1).txt.