Powershell, rename-item doesn't work as expected

时光毁灭记忆、已成空白 提交于 2020-01-11 06:44:10

问题


I have a bunch of jpg image files named in the following pattern:

0001-rand01_012.jpg
0002-rand03_034.jpg

I want to rename them by removing the first 5 characters to get the form:

rand01_012.jpg

etc..

I use the following command:

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

When using this with -whatif flag i get the expected message saying:

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

But removing the whatif gives me errors of this type:

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.

followed by a whole bunch of:

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

The files themselves are renamed with random number of characters removed rather than 5 as was intended. So they have ended up like:

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

etc.

I have used this command to rename such files in the past with success. The fact that I'm getting such random results is making me pull my hair out.


回答1:


tl;dr

Make sure you only process the files of interest:

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

Placing (...) around Get-ChildItem isn't technically necessary, but advisable. [1]

That way:

  • You rule out unrelated files up front.

  • Even if something goes wrong, you can correct the problem and run the command again to reprocess only the failed files, without affecting the previously renamed files.

    • The most likely reason for something going wrong is more than 1 input file resulting in the same filename after removing the 5 first char.

It sounds like you've mistakenly run the command repeatedly, so you've cut off 5 chars. multiple times:

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

Once a filename has fewer than 5 chars., you'll get the the startIndex-related error, because the [string] class's .Substring() method doesn't accept an index beyond the length of the string (try 'ab'.Substring(3)).

That said, since you're running Get-ChildItem without a filter and therefore return all (non-hidden) child items, you may be processing unrelated files ore even directories whose names are too short.

The Cannot create a file when that file already exists. errors are just follow-on errors that result from the script block that normally returns the new name effectively returning the empty string, so Rename-Item is somewhat obscurely complaining that you can't rename a file to its current name.

That said, you can even get Cannot create a file when that file already exists errors during the first run, namely if more than 1 input file with its first 5 chars. chopped off results in the same filename.

E.g., 0001-rand01_012.jpg and 0002-rand01_012.jpg would both be renamed to rand01_012.jpg, which fails once the first one has been renamed.

That is, for your command to work as intended, all filenames that result from dropping the first 5 chars. must be unique.

Here's an MCVE (Minimal, Complete, and Verifiable Example):

Setup:

# 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" }

1st run:

# 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

A 2nd run yields:

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

At the time of the 3rd run, all the names are too short, and all you'll get is Rename-Item : Cannot create a file when that file already exists. errors.


[1] Enclosing Get-ChildItem in (...) ensures that the matching files are collected in an array, up front, before Rename-Item is invoked.
This explicitly prevents already-renamed files from getting re-enumerated by Get-ChildItem and thus interfering with the iteration; explicit use of (...) is technically not necessary, because Get-ChildItem is implemented in a way that always internally collects all files up front, across platforms, because it sorts them by name, which is inherently only possible after all names have been collected.
In light of that, whether you use (...) or not should functionally amount to the same, although using (...) is advisable, because it doesn't rely on an implementation detail.



来源:https://stackoverflow.com/questions/42470793/powershell-rename-item-doesnt-work-as-expected

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!