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

PowerShell批量文件重命名中的奇怪行为,powershell,rename,renaming,Powershell,Rename,Renaming,我是PowerShell的新手,最近我尝试重命名文件夹中的120个文件,但遇到了一个奇怪的行为 我拥有的是名为0001.txt,0002.txt。。。0120.txt共有120个文件。我想在每个文件名前面添加一个“a”。我想出了这个命令: ls | ren -newname {$_.name -replace '(\d+)','a$1'} 但是在执行之后,我得到了很多这样的错误: 指定的路径、文件名或两者都太长。完全限定的文件名必须小于260 当我查看我的文件夹时,我得到的只是来自 aaaaa

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


有人能告诉我重命名命令中是否有错误吗?

将管道连接到每个对象(简写为%{})以批量重命名文件。如果只想在每个文件名前加字母a,则不需要正则表达式,只需执行以下操作:

Get-ChildItem | %{Rename-Item $_ ('a' + $_.name)}
使用首选别名:

ls | %{ren $_ ('a' + $_.name)}
要使用正则表达式执行此操作,使用
($\u.name-replace'^','a')
更简单。如果使用正则表达式的原因是目录中有其他文件,并且您只想重命名以数字字符串和扩展名.txt命名的文件,请注意,您使用的正则表达式会将a前置到任何连续数字字符串。例如,agent007.txt将重命名为agent007.txt。您应该让正则表达式只匹配您想要的格式:
'^(\d+)\.txt'

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

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

更新:PowerShell 3.0中似乎存在一个bug,在某些情况下可能导致批量文件重命名失败。如果通过管道将目录列表重命名为重命名项,则任何重命名为字母顺序高于其当前名称的文件都将按其新名称重新处理,正如稍后在目录列表中遇到的那样。这可能会导致重复重命名相同的文件,直到完整路径的长度超过最大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 ObjectGet ChildItem筛选列表,以便排除新文件名。更新部分在PowerShell 3.0中运行之前的上述命令的最后版本。据推测,重命名的文件将通过其新名称重新引入管道并再次处理,但由于新名称与过滤器
?{$\u-match'^(\d+\.txt'}
不匹配,因此它们不会在第二次通过管道时重命名,也不会再次重新处理。

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

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

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

(ls) | ren -newname {$_.name -replace '(\d+)','a$1'}
以下是另一种在文件名前加上“a”前缀的方法:

(ls) | ren -newname { $_.name -replace '^','a' } -whatif
或者使用ps 6-替换(则不需要括号):


谢谢你的回答,你的解释对我这样的新手来说是非常有用的。您的命令在PowerShell版本2上确实有效(我也是)。但它在PowerShell的第3版上给出了相同的行为。我可以知道您使用的是哪个版本的PowerShell吗?在版本3中,如果我将文件夹中的文件减少到20个,这种奇怪的行为就不会出现。我可能刚刚在PowerShell中发现了一个bug,或者我有一个错误的ram芯片哇,你说得对!这是PS3中的一个bug。我在两个版本中都进行了测试,但只测试了少量文件。对于较大的数字,似乎正在发生的是,如果一个文件被重命名为字母顺序较高的某个文件,它将按其新名称再次进行处理—在这种情况下,相同的文件会重复出现这种情况。评论时间太长,但我将更新我的答案,并向Microsoft提交错误报告。现在,我可以告诉你,我的答案中的最后一个版本,带有where过滤器,确实有效,因为新名称与模式不匹配,所以它们不会被重新处理。谢谢你的反馈和冗长的更新。第一个发现bug感觉很好:
ls | ren -newname { $_.name -replace '.+',{"a$_"} } -whatif