How do I remove periods from file names using PowerShell? - powershell

I'm trying to write a script in PowerShell that searches folders for files containing unnecessary periods, then mass removes the periods from the file names.
ex. Example.File.doc ---> ExampleFile.doc
Whenever I run the code, the console returns the following: "Rename-Item : Cannot bind argument to parameter 'NewName' because it is an empty string."
Does anyone know what the issue is?
Thanks in advance for any help!
$files = get-childitem -path "C:\XXXX\XXXX\XXXX" -Recurse
foreach($file in $files) {
if($file.Name -match ".") {
$newName = $file.Name -ireplace (".", "")
Rename-Item $file.FullName $newName
Write-Host "Renamed" $file.Name "to $newName at Location:" $file.FullName
}
}

One thing about string replacing: When I tried your example without escaping the period, it didn't replace anything and returned nothing (empty string), which I believe answers your "Cannot bind argument to parameter 'NewName' because it is an empty string"
This worked by escaping the period. Also, it works with or without the parenthesis.
$string = "the.file.name"
$newstring = $string -ireplace "\.", ""
// output: thefilename
$newstring.length
// output: 11
$othernewstring = $string -ireplace ("\.", "")
// output: thefilename
$othernewstring.length
// output: 11
// What the OP tried
$donothingstring = $string -ireplace (".", "")
// output:
$donothingstring.length
// output: 0
There is some additional information on string replace here, just for reference https://vexx32.github.io/2019/03/20/PowerShell-Replace-Operator/

$file.Name -ireplace (".", "")
invariably returns an empty string, because the -replace operator (-ireplace is just an alias[1]) operates on regexes (regular expressions), in which metacharacter . matches any character[2].
Since -replace always replaces all matches it finds, all characters in the input are replaced with the empty string, yielding an empty string overall, and passing an empty string via $newName to the (positionally implied) -NewName parameter of Rename-Item predictably causes the error message you saw.
To match a . character verbatim in a regex, you must escape it as \.:
$file.Name -replace '\.', '' # Note: no (...) needed; `, ''` optional
Note that it's generally better to use '...' quoting rather than "..." for regexes and string literals meant to be used verbatim.
However:
This would also remove the . char. in the filename extension, which is undesired.
Also, your command can be streamlined and simplified, as the solution below shows.
A concise, single-pipeline solution:
Get-ChildItem -File -Path "C:\XXXX\XXXX\XXXX" -Recurse -Include *.*.* |
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.
-File limits matching to files (not also directories).
-Include *.*.* limits matching to file names that contain at least 2 . chars.
Piping the files to rename directly from Get-ChildItem to Rename-Item allows you to specify the -NewName argument as a delay-bind script block ({ ... }), which can dynamically derive the new name from the name of the respective input file, using the properties of the System.IO.FileInfo instances received from Get-ChildItem.
$_.BaseName -replace '\.' removes all literal . chars. (escaped as \. in the context of the regex (regular expression) that the -replace operator operates on) from the file base name (the part without the (last) filename extension).
Note: not specifying a replacement operand is the same as specifying '', the empty string, resulting in the effective removal of the matched string; in other words: ... -replace '\.' is the same as ... -replace '\.', ''
+ $_.Extension appends the original extension to the modified base name.
[1] -replace is case-insensitive by default, as all PowerShell operators are; -ireplace just makes that fact explicit; there's also the -creplace variant, where the c indicates that the operation is case-sensitive.
[2] in single-line strings; in multi-line strings, . by default doesn't match \n (newlines), though this can be changed via inline matching option (?s) at the start of the regex.

There are a few other issues here. You should filter your Get-ChildItem results to return only files by including the -File switch.
Your current script will replace the last . before your file's extension which will cause some problems. You need to use the $File.BaseName and $File.Extension attributes to address this problem.
Substitute -ireplace with the .replace() method.
Lastly, you need to use the -like condition in your If statement. The -match condition is used for regexes and will not work correctly here.
$files = get-childitem -Recurse -File
foreach($file in $files) {
if($file.BaseName -like "*.*") {
$newName = $file.BaseName.Replace(".","") + $file.Extension
Rename-Item $file.FullName $newName
Write-Host "Renamed" $file.Name "to $newName at Location:" $file.FullName
}
}

Here's another solution that takes advantage of PowerShell's pipeline (remove the -WhatIf after you've tested which files will be renamed).
It also uses a lookahead regular expression to replace all but the last dot in the filename.
Get-ChildItem -Recurse `
<# Only select files that have more than one '.' in their name #> `
| Where-Object { ($_.Name.ToCharArray() | Where-Object {$_ -eq '.'} | Measure-Object ).Count -gt 1 } `
`
<# Change the location to the directory and rename so you don't have deal with the directory name #> `
<# Also, use a lookahead regex to replace all but the last dot. #> `
| ForEach-Object { Push-Location $_.Directory; Rename-Item $_ ($_.Name -replace "\.(?=.*?\.)", "") -Verbose -WhatIf; Pop-Location }
Here's a more concise version of the same commands using aliases:
dir -r | ?{ ($_.Name.ToCharArray() | ?{ $_ -eq '.' } | measure ).Count -gt 1 } | %{ pushd $_.Directory; ren $_ ($_.Name -replace "\.(?=.*?\.)", "") -v -wh; popd }

Related

Replacing only part of the file name in Powershell

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.

How to add a symbol in a middle of a file name in PS

I have modified several thousand files with a various things requested by the owners.
Now, I need to add one final thing and I am not 100% on how to do it.
Scenario is as follows - all files have a 10 digit number at the start, I need to add a hyphen after the number. String is a variable but it is always the same length.
1234567890abcdefgh.xls would be an example
I have used GCI to make changes to symbols and static parts but not sure how to call for insertion in a specific place of a file (after the 10th character of a variable string)
Any ideas would be most welcome!
You can use the $matches you get from the capturing groups of a -match comparison:
(Get-ChildItem -Path 'X:\WhereTheFilesAre' -File) |
Where-Object { $_.BaseName -match '^(\d{10})([^-].*)' } |
Rename-Item -NewName { '{0}-{1}{2}' -f $matches[1], $matches[2], $_.Extension }
or by using the Substring() method:
(Get-ChildItem -Path 'X:\WhereTheFilesAre' -File) |
Where-Object { $_.BaseName -match '^\d{10}[^-]' } |
Rename-Item -NewName { $_.Name.Substring(0,10) + '-' + $_.Name.Substring(10) }
or use the regex -replace operator:
(Get-ChildItem -Path 'X:\WhereTheFilesAre' -File) |
Where-Object { $_.BaseName -match '^\d{10}[^-]' } |
Rename-Item -NewName { $_.Name -replace '^(\d{10})', '$1-' }
You can use string.Insert() to insert a string into another at a specific offset:
PS ~> '1234567890abcdefgh.xls'.Insert(10, '-')
1234567890-abcdefgh.xls
To apply to all files in a directory, you could do something like this:
Get-ChildItem -File |Where-Object Name -match '^\d{10}[^-]' |Rename-Item -NewName { $_.Name.Insert(10, '-') }
The regular expression pattern ^\d{10}[^-] will only match file names that start with 10 digits followed by something other than a hyphen (to avoid renaming files that already comply with the naming convention)

Replace Part of a File Name in Powershell

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]

Renaming files with powershell to remove a string

I have a bunch of files with the same string in them and would like to remove them
Dir | Rename-Item -NewName { $_.Name -replace " (my string here)","" }
However, this does not seem to be removing the string from all the files that have that string in the file name. Do I need ReGex?
There is a space before the ( which makes me think I need ReGex in some way.
So I was right about needing ReGex with how Powershell does stuff:
Dir | Rename-Item -NewName { $_.Name -replace "(\s*) \(STRING HERE\)","" }
-replace does indeed use regex(case-insensitive).
You can achieve the same result without regex with the use of the .replace which is a string method like so:
Get-ChildItem -path 'your path' |
Rename-Item -NewName { $_.BaseName.replace(' (my string here)','') + $_.extension } -WhatIf
notice the use of $_.basename instead of name. this is because name consists of both filename and extension whereas basename does not.

Powershell replacing periods

Can anyone tell me if they think there is something wrong with this Powershell script.
Dir |
where {$_.extension -eq ".txt"} |
Rename-Item –NewName { $_.name –replace “.“,”-” }
For each text file in the current directory, I'm trying to replace all periods in the file names with hyphens. Thanks ahead of time.
As the others have said, -replace uses regex (and "." is a special character in regex). However, their solutions are forgetting about the fileextension and they are acutally removing it. ex. "test.txt" becomes "test-txt" (no extension). A better solution would be:
dir -Filter *.txt | Rename-Item -NewName { $_.BaseName.replace(".","-") + $_.Extension }
This also uses -Filter to pick out only files ending with ".txt" which is faster then comparing with where.
-replace operates on regex, you need to escape '.':
rename-item -NewName { $_.Name -replace '\.', '-' }
Otherwise you will end up with '----' name...
The name property of a file object in powershell is of type string.
That means, you could also use the static string method replace like follows and don't care about regex' reserved characters:
dir | ? { $_.extension -eq ".txt" } | rename-item -newname { $_.name.replace(".","-") }