If you do not specify a second (length) parameter to the Substring()
function, it will return the remainder of the string taken from the first parameter (the character index), so you can simply do:
Get-ChildItem -File | ForEach-Object { $_ | Rename-Item -NewName $_.Name.SubString(4)}
The error you got "Index and length must refer to a location within the string."
means that the second parameter (the wanted length) exceeds the total length of the string, because you are cutting off the first 4 characters.
$_.Name.SubString(4,$_.Name.Length - 4)
would work, but is overkill in this case.
EDIT
Given the OPs comments, I tested some more and indeed... There seems to be a problem with piping the results from Get-ChildItem
directly to the Rename-Item
cmdlet. (I'm using Powershell 5.1)
It seems you need to capture the items from the Get-ChildItem
cmdlet and iterate that captured collection in order to rename te files. Otherwise, some files could be processed and renamed more than once.
You can capture the collection of files in a variable first like this:
$files = Get-ChildItem -File
foreach($file in $files) { $file | Rename-Item -NewName $file.Name.SubString(4)}
Or by enclosing the Get-ChildItem
part in brackets as PetSerAl suggested:
(Get-ChildItem -File) | Rename-Item -newname { $_.Name.SubString(4) }
I found an explanation for this in this answer:
There appears to be a bug that can cause bulk file renaming to fail under certain conditions. If the files are renamed by piping a directory listing to Rename-Item, any file that's renamed to something that's alphabetically higher than its current name is reprocessed by its new name as it's encountered later in the directory listing.