5

我是 PowerShell 的新手,最近我尝试重命名文件夹中的 120 个文件,但遇到了一个奇怪的行为。

我所拥有的是名为 0001.txt、0002.txt、... 0120.txt 的文件,共有 120 个文件。我想在每个文件名前面添加一个“a”。我想出了这个命令:

ls | ren -newname {$_.name -replace '(\d+)','a$1'}

但执行后,我收到很多这样的错误:

“指定的路径、文件名或两者都太长。完全限定的文件名必须小于 260”

当我查看我的文件夹时,我得到的只是文件名

aaaaaaaaaaaaaaaaaaaaaaaaa(repeat until it hit system limit on path length)....a0001.txt
...
...
...
aaaaaaaaaaaaaaaaaaaaaaaaa(repeat until it hit system limit on path length)....a0120.txt

使用 -cf 开关进一步检查后,发现 PowerShell 递归地尝试重命名过程。在重命名所有 120 个文件的第一遍之后,它再次将命令应用于 a0001.txt,有效地在文件名前添加了另一个“a”。这种情况一直持续到达到路径长度限制并报告错误。

谁能告诉我我的重命名命令是否有问题?

4

3 回答 3

5

管道到Foreach-Object(简写%{ })以批量重命名文件。如果您只想在每个文件名前面加上字母a,则不需要正则表达式,您需要做的就是:

Get-ChildItem | %{Rename-Item $_ ('a' + $_.name)}

使用您喜欢的别名:

ls | %{ren $_ ('a' + $_.name)}

要使用正则表达式,使用更简单($_.name -replace '^','a')。如果使用正则表达式的原因是目录中有其他文件,并且您只想重命名以数字字符串和.txt扩展名命名的文件,请注意您正在使用的正则表达式会在任何字符串前面加上a的连续数字。例如,agent007.txt将被重命名为agenta007.txt。您应该让正则表达式仅匹配您想要的格式:'^(\d+)\.txt'.

另请注意,通过在-NewName参数中使用正则表达式替换,您将每个与正则表达式不匹配的文件重命名为其已有的名称。对于 120 个文件来说没什么大不了的,但我会提前过滤文件列表:

Get-ChildItem | ?{$_ -match '^(\d+)\.txt'} | %{Rename-Item $_ ('a' + $_.name)}

更新: PowerShell 3.0 中似乎存在一个错误,可能导致批量文件重命名在某些情况下失败。如果通过将目录列表传递给Rename-Item来重命名文件,则任何重命名为按字母顺序高于其当前名称的文件都会被其新名称重新处理,因为它稍后会在目录列表中遇到。这可能会导致相同的文件被重复重命名,直到完整路径的长度超过 260 个字符的最大值并引发错误。

请注意,例如,如果字母a是附加而不是前置,则没有问题。这适用于任意数量的文件:

Get-ChildItem | %{Rename-Item $_ ($_.name + 'a')}

如果文件名为b0001b0002等,并且前面有字母a,则不会发生重复的重新处理。但是,如果前面有字母c,它确实会出现。

如果所有文件的名称都以b开头,则以下命令将单个a添加到任意数量的文件的开头:

Get-ChildItem | %{Rename-Item $_ ('a' + $_.name)}

给定名称以b开头的相同文件,以下命令会重复添加字母c直到达到长度限制:

Get-ChildItem | %{Rename-Item $_ ('c' + $_.name)}

这只发生在包含一定数量或更多文件的列表中。OP说如果他将文件数量减少到20个以下就没有问题。我发现阈值是37(36个文件或更少,重新处理不会发生)。我尚未确定所有适用案例的数字是否相同或取决于更具体的因素。

稍后我将对此进行详细说明,并在我更具体地确定此问题的参数后向 Microsoft 提交错误报告。

解决方法:使用Where-Object从Get-ChildItem过滤列表,以便排除新文件名。更新部分之前的上述命令的最后一个版本适用于 PowerShell 3.0。据推测,重命名的文件会以其新名称重新引入管道并再次处理一次,但由于新名称与过滤器不匹配,因此它们不会在第二次通过管道时重命名,也不会再次重新处理。?{$_ -match '^(\d+)\.txt'}

于 2013-08-18T16:52:29.103 回答
1

我会建议一个简单的解决方法,它对我来说很好,没有任何额外的正则表达式和过滤,任何文件编号

$files = Get-ChildItem; $files | ForEach-Object {Rename-Item $_ ('a' + $_.Name)}
于 2020-02-21T13:05:47.350 回答
1

或者在 ls 或 get-childitem 周围使用括号,以便它首先完成。此问题已在 powershell 6 中得到解决。

(ls) | ren -newname {$_.name -replace '(\d+)','a$1'}

这是在文件名前加上“a”前缀的另一种方法:

(ls) | ren -newname { $_.name -replace '^','a' } -whatif

或使用 ps 6 -replace (则不需要括号):

ls | ren -newname { $_.name -replace '.+',{"a$_"} } -whatif
于 2020-02-21T14:36:41.127 回答