【问题标题】:Weird behaviour in PowerShell bulk file renamingPowerShell 批量文件重命名中的奇怪行为
【发布时间】:2013-08-20 12:16:42
【问题描述】:

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

谁能告诉我我的重命名命令有什么问题吗?

【问题讨论】:

    标签: powershell rename renaming


    【解决方案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
    

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案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 中似乎存在一个错误,可能导致在某些情况下批量文件重命名失败。如果通过将目录列表传递给 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'} 不匹配,因此它们不会在第二次通过管道时重命名,也不会再次重新处理.

        【讨论】:

        • 感谢您的回答,您的解释对于像我这样的新手来说非常有用。您的命令在 PowerShell 版本 2 上确实有效(和我的一样)。但它在 PowerShell 版本 3 上给出了相同的行为。我可以知道您使用的是哪个版本的 PowerShell 吗?在版本 3 中,如果我将文件夹中的文件减少到 20 个文件,则不会出现这种奇怪的行为。我可能刚刚在 PowerShell 中发现了一个错误,或者我的内存芯片有问题
        • 哇,你是对的!这 PS 3 中的一个错误。我在两个版本中都进行了测试,但只测试了少量文件。对于较大的数字,似乎正在发生的事情是,如果一个文件被重命名为按字母顺序排列的更高的文件,它会通过它的新名称再次处理 - 在这种情况下,相同的文件会重复发生这种情况。评论太长了,但我会更新我的答案,并向 Microsoft 提交错误报告。现在,我可以告诉你,我的答案中的最后一个版本使用 where 过滤器确实有效,因为新名称与模式不匹配,因此它们不会被重新处理。
        • 感谢您的反馈和长时间的更新。成为第一个发现错误的人感觉很好。 :) 只是想知道为什么这个错误还没有被带出来。
        • 昨天我只是随意将文件数减少到20并发现没有错误。就我而言,这绝不是门槛。但是,我刚刚做了一个快速测试,发现如果文件夹中有 36 个文件,重命名的文件将是 aa0001.txt 到 aa0035.txt(意味着它在第二次迭代时停止),但是第 36 个文件被正确重命名到 a0036.txt。如果我的文件夹中只有 35 个文件,那么一切都会完美无缺。 37 个文件在我的开篇帖子中会有相同的错误。
        • 感谢您提供有关 PowerShell 错误及其解决方法的信息,这让我发疯了!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-30
        • 2012-08-31
        • 2017-06-22
        • 2017-12-10
        • 2010-11-19
        相关资源
        最近更新 更多