Powershell,重命名项目没有按预期工作

Powershell, rename-item doesn't work as expected

我有一堆按以下模式命名的 jpg 图像文件:

0001-rand01_012.jpg
0002-rand03_034.jpg

我想通过删除前 5 个字符来重命名它们以获取以下形式:

rand01_012.jpg

等..

我使用以下命令:

Get-ChildItem | Rename-Item -NewName {$_.name.Substring(5)}

将它与 -whatif 标志一起使用时,我收到预期的消息:

Performing the operation "Rename File" on target "Item: C:\Users\xxxx\Documents\xx xx\temp223-rand16_030.jpg Destination: C:\Users\
xxxx\Documents\xx xx\temp2\rand16_030.jpg".

但是删除 whatif 会给我这种类型的错误:

Rename-Item : The input to the script block for parameter 'NewName' failed. Exception calling "Substring" with "1" argument(s): "startIndex cannot be 
larger than length of string.

接着是一大堆:

Rename-Item : Cannot create a file when that file already exists.

文件本身被重命名,删除了随机数量的字符,而不是预期的 5 个。所以他们最终变成了这样:

01.jpg
01.jpg
.
.
.
d14_001.jpg

等等

我过去曾使用此命令成功重命名此类文件。我得到如此随机的结果这一事实让我抓狂。

tl;dr

确保只处理感兴趣的文件:

(Get-ChildItem -File [0-9][0-9][0-9][0-9]-*.jpg) |
  Rename-Item -NewName {$_.name.Substring(5)} -WhatIf

上面命令中的-WhatIf common parameter预览操作。一旦您确定该操作将执行您想要的操作,请删除 -WhatIf
PowerShell [Core] 6+ 中,将 (...) 放置在 Get-ChildItem 周围不再是 技术上 所必需的,而是可取的。[ 1]

这样:

  • 您预先排除了不相关的文件。

  • 即使出现问题,您也可以更正问题并再次运行命令以仅重新处理失败的文件,而不会影响之前重命名的文件。

    • 出现问题的最可能原因是超过 1 个输入文件导致在删除第 5 个字符后 相同 文件名。

听起来你错误地 运行 命令 重复 ,所以你截掉了 5 个字符。 多次

0001-rand01_01.jpg -> rand01_01.jpg -> _01.jpg

一旦文件名少于 5 个字符,您将得到与 startIndex 相关的错误,因为 [string] class 的 .Substring() 方法不接受超出字符串长度的索引(尝试 'ab'.Substring(3))。

也就是说,由于您 运行宁 Get-ChildItem 没有过滤器,因此 return all (非隐藏)子项,您可能正在处理 不相关的文件或什至是名称太短的目录

Cannot create a file when that file already exists. 错误只是由通常 return 有效地 return 空字符串 return 新名称的脚本块导致的后续错误,因此 Rename-Item 有点含糊地抱怨您不能将文件重命名为当前名称。

也就是说,在 第一个 运行 期间,您甚至可能会遇到 Cannot create a file when that file already exists 错误,即如果有超过 1 个输入文件它的前 5 个字符。截断结果相同的文件名。

例如,0001-rand01_012.jpg0002-rand01_012.jpg 都将重命名为 rand01_012.jpg,一旦第一个重命名,就会失败

也就是说,为了让您的命令按预期工作,删除前 5 个字符产生的所有文件名。必须是唯一的。

这是一个 MCVE (Minimal, Complete, and Verifiable Example):

设置:

# Create and change to temp dir.
Set-Location (mkdir temp)
# Create sample input files named 0001-rand01_01.jpg, 0002-rand01_02.jpg, ...
# Note how the suffixes after the first 5 char. must be *unique*.
1..9 | %{ $null > "000${_}-rand01_0${_}.jpg" }

第一 运行:

# No errors
> (Get-ChildItem -File) | Rename-Item  -NewName { $_.Name.Substring(5) }
# Show new names
> Get-ChildItem | Select Name

Name         
----         
rand01_01.jpg
rand01_02.jpg
rand01_03.jpg
rand01_04.jpg
rand01_05.jpg
rand01_06.jpg
rand01_07.jpg
rand01_08.jpg
rand01_09.jpg

第二个 运行 产量:

Name    
----    
1_01.jpg
1_02.jpg
1_03.jpg
1_04.jpg
1_05.jpg
1_06.jpg
1_07.jpg
1_08.jpg
1_09.jpg

第3次运行的时候,所有的名字都太短了,只会出现Rename-Item : Cannot create a file when that file already exists.个错误


[1] 在 (...) 中包含 Get-ChildItem 确保匹配的文件收集在 array, 预先,在Rename-Item被调用之前。
这明确地防止了已经重命名的文件被 Get-ChildItem 重新枚举,从而干扰迭代。 (...) 的显式使用 在技术上 在 PowerShell [Core] 6+ 中不再需要(它 中是 所必需的Windows PowerShell (5.1-)),因为 Get-ChildItem 是以 always internally collects info about all files up front, across platforms 的方式 实现的 ,因为它将它们排序名称,这本质上只有在收集完所有名称后才有可能。
鉴于此,无论你是否使用(...)功能上应该是一样的,尽管使用(...)是可取的,因为它不依赖于什么相当于 实现细节 documentation 没有提到输出是如何排序的)。