File rename key off the first underscore to the next period - powershell

How can I filter on the first underscore and the following period
Here is what I have so far but the file I'm receiving is changing, in some cases the original filename has an extra underscore. I need a way to account for that.
Get-ChildItem "\\MyFileServer\*" | Rename-Item -NewName { ($_.Name -replace '(?<=^.{3}).{5}', '.').Replace(".vfmpclmadj.", ".sa.") }
original filename
999_987895_888888_544P.44444.vfmpclmadj.000025001.20201216.175314
New filename
999.44444.sa.000025001.20201216.175314

Something like this should work.
('999_987895_888888_544P.44444.vfmpclmadj.000025001.20201216.175314' -replace '_.+?(?=\.)').Replace(".vfmpclmadj.", ".sa.")
It simply looks for an underscore plus any characters up to a period. You could make it more strict but for this example it wasn't needed. Something like this would also work but only on the first underscore. The former could potentially affect other underscores later in the string.
('999_987895_888888_544P.44444.vfmpclmadj.000025001.20201216.175314' -replace '(?<=^[^_]+)_.+?(?=\.)').Replace(".vfmpclmadj.", ".sa.")

Related

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

How to replace first characters in a file name with a string?

I've been working on a script to maintain the archive from my IP camera DVR. My recording software outputs filenames formatted so that the first character is the camera number, followed by a date and time stamp.
ex. 1_2017-11-03_00-45-07.avi
I want to replace the first character with a string that represents the camera.
ex. DivertCam_2017-11-03_00-45-07.avi
So far, I have:
Get-ChildItem "D:\DivertCam\1_*.avi" |
Rename-Item -NewName {$_.Name -replace '1_?','DivertCam_'}
Luckily with -WhatIfand running a transcript, I was able to see that my results would be wrong:
What if: Performing the operation "Rename File" on target "Item: D:\DivertCam\1_2017-11-03_00-45-07.avi Destination: D:\DivertCam\DivertCam_20DivertCam_7-DivertCam_DivertCam_-03_00-45-07.avi"
I know it's just picking out every "1_". How can I make it after the the first instance of "1_", or read the filename like a string, split it into 3 arrays separated by "_" and then change the first array?
The -replace operator performs a RegEx match and replacement, so you can use RegEx syntax to do what you want. For you the solution is to include the 'beginning of string' characater ^ at the beginning of your match text. Since this is RegEx, the ? means the previous character may or may not exist, so what you are currently matching on is any character matching '1' which may or may not be followed by an underscore. A better version would simply be:
$_.name -replace '^1','DivertCam'
To put that in context with the rest of your line, it would be:
Get-ChildItem "D:\DivertCam\1_*.avi" | Rename-Item -NewName {$_.name -replace '^1','DivertCam'}
Keep in mind this only works for the -replace operator which uses RegEx (short for Regular Expression) matching, and not the .Replace() method that you may see used, which uses simple pattern matching.
This will replace everything before the first '_' with 'DivertCam' (note use of % (foreach) to operate on each file individually).
Get-ChildItem "D:\DivertCam\1_*.avi" | % {Rename-Item $_.FullName -NewName "DivertCam$($_.Name.Substring($_.Name.IndexOf('_')))" }

Pipes in replace causing line to be duplicated

I have a script that I need to replace a couple of lines in. The first replace is going fine but the second is wiping out my file and duplicating the line multiple times.
My code
(get-content $($sr)) -replace 'remoteapplicationname:s:SHAREDAPP',"remoteapplicationcmdline:s:$($sa)" | Out-File $($sr)
(get-content $($sr)) -replace 'remoteapplicationprogram:s:||SHAREDAPP',"remoteapplicationprogram:s:||$($sa)" | Out-File $($sr)
The first replace works perfectly. The second one is causing this:
remoteapplicationprogram:s:||stagaredrremoteapplicationprogram:s:||stagarederemoteapplicationprogram:s:||stagareddremoteapplicationprogram:s:||stagarediremoteapplicationprogram:s:||stagaredrremoteapplicationprogram:s:||stagarederemoteapplicationprogram:s:||stagaredcremoteapplicationprogram:s:||stagaredtremoteapplicationprogram:s:||stagaredcremoteapplicationprogram:s:||stagaredlremoteapplicationprogram:s:||stagarediremoteapplicationprogram:s:||stagaredpremoteapplicationprogram:s:||stagaredbremoteapplicationprogram:s:||stagaredoremoteapplicationprogram:s:||stagaredaremoteapplicationprogram:s:||stagaredrremoteapplicationprogram:s:||stagareddremoteapplicationprogram:s:||stagared:remoteapplicationprogram:s:||stagarediremoteapplicationprogram:s:||stagared:remoteapplicationprogram:s:||stagared1remoteapplicationprogram:s:||stagared
etc...
Is this because of the ||? If so, how do I get around it?
Thanks!
To begin with, you should be using slightly more meaningful names for your variables. Especially if you want someone else to be reviewing your code.
The gist of your issue is that -replace supports regexes (regular expressions), and you have regex control characters in your pattern string. Consider the following simple example, and notice everywhere the replacement string is found:
PS C:\Users\Matt> "ABCD" -replace "||", "bagel"
bagelAbagelBbagelCbagelDbagel
-replace is also an array operator, so it works on every line of the input file, which is nice. For simplicity's sake, if you are not using a regex, you should just consider using the string method .Replace(), but it is case-sensitive, so that might not be ideal. So let's escape those control characters in the easiest way possible:
$patternOne = [regex]::Escape('remoteapplicationname:s:SHAREDAPP')
$patternTwo = [regex]::Escape('remoteapplicationprogram:s:||SHAREDAPP')
(get-content $sr) -replace $patternOne, "remoteapplicationcmdline:s:$sa" | Out-File $($sr)
(get-content $sr) -replace $patternTwo, "remoteapplicationprogram:s:||$sa" | Out-File $($sr)
Now we get both patterns matched as you have them written. Run $patternTwo on the console to see what has changed to it! $patternOne, as written, has no regex control characters in it, but it does not hurt to use the escape method if you are just expecting simple matching.
Aside from the main issue pointed out, there is also some redundancy and misconception that can be addressed here. I presume you are updating a source file to replace all occurrences of those strings, yes? Well, you don't need to read the file in twice, given that you can chain -replace:
$patternOne = [regex]::Escape('remoteapplicationname:s:SHAREDAPP')
$patternTwo = [regex]::Escape('remoteapplicationprogram:s:||SHAREDAPP')
(get-content $sr) -replace $patternOne, "remoteapplicationcmdline:s:$sa" -replace $patternTwo, "remoteapplicationprogram:s:||$sa" |
Set-Content $sr
Perhaps that will do what you intended.
You might notice that I've removed the subexpressions operators ($(...)) around your variables. While they have their place, they don't need to be used here. They are only needed inside more complicated strings, like when you need to expand object properties or something.