使用PowerShell递归重命名文件

37

我目前有一条命令行用于在当前文件夹中批量重命名文件。

dir | foreach { move-item -literal $_ $_.name.replace(".mkv.mp4",".mp4") }

这段代码在我当前所在的任何目录都能完美运行,但是我想从包含11个子文件夹的父文件夹中运行脚本。我可以一个个导航到每个文件夹来完成任务,但我更愿意只运行一次脚本然后就完成了。

我尝试了以下方法:

get-childitem -recurse | foreach { move-item -literal $_ $_.name.replace(".mkv.mp4",".mp4") }

有人能指点我正确的方向吗?我对Powershell并不是很熟悉,但在这种情况下它很适合我的需求。


1
可能是在Powershell中递归重命名文件的重复问题。 - Anthony Neace
1
谢谢你指引我到那个帖子。然而,即使我已经阅读了其他问题的答案,我仍然遇到困难。我要么得到文件不存在的错误,要么得到文件正在被另一个进程使用的错误。 - Matthew
3个回答

66

你离正确很近:

Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace(".mkv.mp4",".mp4")}

谢谢您的回答,但我现在遇到了以下问题:`Move-Item: 进程无法访问该文件,因为它正在被另一个进程使用。 在第1行第45个字符处:
  • get-childitem -recurse | foreach { move-item <<<< -literal $.FullName $.FullName.replace(".mkv.mp4",".mp4") }
    • CategoryInfo : WriteError: (D:\The Wire\Season 1:DirectoryInfo) [Move-Item], IOException
    • FullyQualifiedErrorId : MoveDirectoryItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand`
- Matthew
@Matthew,我相信这是因为你使用了move-item而不是rename-item,我已经修复了我的帖子。 - Cole9350
无法重命名,因为“Settings.png”的项目不存在。它可以在根目录中正常工作,但在子文件夹中则失败。 - The Muffin Man
当目标文件已经存在时,操作将失败。要覆盖现有文件,请使用 Move-Item,如此处所示 https://dev59.com/01wY5IYBdhLWcg3ws5kF - Skorunka František
如果您使用-LiteralPath而不是-Path,则可以处理其中包含括号的路径。 - dsmtoday
显示剩余4条评论

41

有一个不太为人所知的功能,专门为这种情况设计。简而言之,您可以这样做:

Get-ChildItem -Recurse -Include *.ps1 | Rename-Item -NewName { $_.Name.replace(".ps1",".ps1.bak") }

通过为参数NewName传递脚本块,避免使用ForEach-Object。PowerShell足够智能,能够对每个经过管道传输的对象评估脚本块,并设置$_,就像使用ForEach-Object一样。


我在PowerShell 2.0中进行了测试,它只适用于1个文件夹,并且不会递归到子文件夹中... - Cole9350
我刚刚更新了代码片段,使其适用于V2版本 - 需要使用-Include参数。V3版本更加智能,不再需要-Include参数。 - Jason Shirk
太棒了,+1,不用foreach就完成了。 - Cole9350
花了很多时间阅读关于foreach等在子文件夹中无法工作的帖子。这是唯一有效的方法。 - The Muffin Man
凭借多年的经验和远见:这个功能现在已经有了一个名称,并且有文档记录:delay-bind script blocks - mklement0

0
请注意,如果您仍然遇到类似于“无法重命名,因为位于 '...' 的项目不存在”等错误,请注意您可能正在处理一些超长的路径或具有“特殊解释”字符(如方括号,即 [ ] )的路径。
对于这种情况,请使用-LiteralPath/-PSPath以及特殊前缀\\?\ (对于UNC路径,您将想要使用前缀\\?\UNC\),以获取长达32k字符的路径。我还建议尽早过滤(使用Get-ChildItem),以提高性能(较少的Rename-Item调用更好)。

$path = 'C:\Users\Richard\Downloads\[Long Path] THE PATH TO HAPPINESS (NOT CLICKBAIT)\...etc., etc.'
# -s is an alias for -Recurse
# -File for files only
# gci, dir, and ls are all aliases for Get-ChildItem
#   Note that among the 3, only `gci` is ReadOnly.
gci -s -PSPath $path -File -Filter "*.mkv.mp4" |
    # ren, rni are both aliases for Rename-Item
    #   Note that among the 2, only `rni` is ReadOnly.
    # -wi is for -WhatIf (a dry run basically). Remove this to actually do stuff.
    # I used -replace for regex (for excluding those super rare cases)
  rni -wi -PSPath { "\\?\$($_.FullName)" } -NewName { $_.Name -replace '\.mkv(?=\.mp4$)','' }

需要注意的是,在_PowerShell [Core] v6+ _ 中,长路径支持现在已默认启用,使用 \\?\ 前缀不仅没必要,还可能会导致问题。 - mklement0
请注意,您的命令同时使用了 gci 的管道输入和 -PSPath (-LiteralPath) 参数,这不会起作用。 - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接