You're not far off. With Rename-Item, you don't need a ForEach-Object loop in this case, because the NewName
parameter can also take a scriptblock to perform an action on every file sent through the pipeline.
Try
$sourcePath = 'D:\Test' # change this to the rootfolder where the files and subfolders are
Get-ChildItem -Path $sourcePath -File -Recurse |
Where-Object { $_.Name -match '^(\D+)(_.*)' } | # see regex details below
Rename-Item -NewName {
# split on the underscore and join the first characters of each part together
$prefix = ($matches[1].Split("_") | ForEach-Object { $_.Substring(0,1) }) -join ''
'{0}{1}' -f $prefix, $matches[2]
}
Regex details:
^ Assert position at the beginning of the string
( Match the regular expression below and capture its match into backreference number 1
\D Match a single character that is not a digit 0..9
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)
( Match the regular expression below and capture its match into backreference number 2
_ Match the character “_” literally
. Match any single character that is not a line break character
* Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
)