Search code examples
powershellsquare-bracketrename-item-cmdlet

Renaming files containing square braces


A PS command for batch renaming of the files in one folder does work for all files in which there is no pair of square brackets but never if the file name contains one. It does work also if one or more right square brackets are in the name, but any number of left brackets cause an error.

The error comment: rni : Impossible de renommer l'élément situé à l'emplacement « C:\Users\X\documents\dossier\machine[3].txt », car il n'existe pas.

translation _ rni: impossible to rename the element found in « C:\Users\X\documents\dossier\machine[3].txt », as it does not exist.

Here is the code of the command;

$dos1=(ls C:\Users\X\documents\dos1).name
foreach ($fic in $dos1)
{rni C:\Users\X\documents\dos1\$fic §§$fic}

User PetSerAI's suggestion to insert "-LiteralPath" works fine for the present case;

$dos1=(ls C:\Users\X\documents\dos1).name
foreach ($fic in $dos1)
{rni -literalpath C:\Users\X\documents\dos1\$fic §§$fic}

Nevertheless, for a somewhat more complicated code the same problem reccurs; "-LiteralPath" does not have the expected effect in the following code;

$dos1=(ls C:\Users\X\documents\dos1).name       
$dos2=(ls C:\Users\X\documents\dos2).name
foreach ($fic2 in $dos2) {foreach ($fic1 in $dos1)
{if ("$fic1" -match "$fic2") {rni -literalpath C:\Users\X\documents\dos1\$fic1 §§$fic1}}}

Worse still, renaming occurs for at least one pair of unlike names:

"§§§§§§§§machine 5.txt", "machine 5.txt".

Is there a way to make that work without too much additional coding ?


Solution

    • As PetSerAl has noted, use the -LiteralPath parameter to unequivocally pass a path as-is, without the potentially unwanted interpretation as a wildcard expression, because that's what positional use of a path argument does, due to implicitly binding to the -Path parameter.

    • Similarly, you mustn't use -match to perform string-literal comparisons, as the RHS of -match is by design interpreted as a regex (regular expression), where [...] has special meaning too, as it does in wildcard expressions (though it's important to note that regexes and wildcards are only distantly related and generally have fundamentally different syntax).

      • To perform string-literal equality comparison, simply use -eq.
      • In case you need string-literal substring matching, see this answer.

    Therefore, use the following:

    $dos1=(gci -LiteralPath C:\Users\X\documents\dos1).name       
    $dos2=(gci -LiteralPath C:\Users\X\documents\dos2).name
    foreach ($fic2 in $dos2) {
      foreach ($fic1 in $dos1) {
       if ($fic1 -eq $fic2) { rni -LiteralPath C:\Users\X\documents\dos1\$fic1 §§$fic1 }
      }
    }
    

    Note: I've replaced ls with the more PowerShell-idiomatic alias gci for the Get-ChildItem cmdlet (consistent with your use of rni for Rename-Item); overall, however, it's better to avoid aliases altogether in scripts.

    Also note that in PowerShell - unlike in POSIX-like shells such as Bash - there's no need to enclose (string) variables in "...", as referencing them as-is works fine, even if they contain embedded whitespace; that is, if $var is a string, use it as-is - no need for "$var".