简体   繁体   中英

How do I remove periods from file names using 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/

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 ).

    • 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.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM