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/
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
}
}
$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 ).
''
, 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.
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 }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.