PowerShell批量文件重命名中的奇怪行为

时间:2013-08-18 16:32:08

标签: powershell rename renaming

我是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'。这一直持续到达到路径长度限制并报告错误。

任何人都可以告诉我,我的重命名命令有什么问题吗?

3 个答案:

答案 0 :(得分:3)

管道到 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中似乎存在一个错误,可能导致批量文件重命名在某些情况下失败。如果通过将目录列表管道重命名为重命名项来重命名文件,则任何重命名为字母高于其当前名称的文件都将按其新名称重新处理,因为稍后将在目录列表中遇到该文件。这可能导致重复重命名相同的文件,直到完整路径的长度超过260个字符并且抛出错误。

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

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

如果文件名为 b0001 b0002 等,并且前面加上字母 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'}不匹配,因此在第二次通过管道时不会重命名它们而不是再次重新处理。

答案 1 :(得分:0)

我建议一个简单的解决方法,它对我来说很好,不需要任何其他正则表达式和过滤,并且文件号为

$files = Get-ChildItem; $files | ForEach-Object {Rename-Item $_ ('a' + $_.Name)}

答案 2 :(得分:0)

或在ls或get-childitem周围使用括号,使其首先完成。在Powershell 6中已解决此问题。

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

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

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

或者用ps 6替换(然后不需要括号):

ls | ren -newname { $_.name -replace '.+',{"a$_"} } -whatif