#PSTip ForEach-Object Gotcha!

Note: This tip requires PowerShell 2.0 or above.

Let’s say you want to rename a bunch of files in a folder and imagine that you used the following code:

Get-ChildItem -Recurse C:\Scripts | ForEach-Object { $count = 0; Rename-Item -Path $_.FullName -NewName "TestScript${Count}$($_.Extension)"; $count++}

What do you think will happen here? You may see numerous error messages that the new file name already exists! Let’s quickly check the syntax of ForEach-Object cmdlet:

PS C:\> Get-Command ForEach-Object -Syntax

ForEach-Object [-Process] <scriptblock[]> [-InputObject <psobject>] [-Begin <scriptblock>] [-End <scriptblock>]
[-RemainingScripts <scriptblock[]>] [-WhatIf] [-Confirm] [<CommonParameters>]

ForEach-Object [-MemberName] <string> [-InputObject <psobject>] [-ArgumentList <Object[]>] [-WhatIf] [-Confirm]
As you see in the above output, the default parameter set of ForEach-Object cmdlet has -Begin, -Process, and -End parameters. When no parameter name is specified, the script block gets assigned to the -Process parameter  and gets executed every time an object is available in the pipeline. So, this behavior causes $count to get re-initialized to zero in every iteration.

So, how do we work around this? This is where the Begin script block comes handy. The script block passed to the -Begin parameter executes only once and it is a good place to initialize $count variable. Let us see how:

Get-ChildItem -Recurse C:\Scripts | ForEach-Object -Begin { $count = 0} -Process { Rename-Item -Path $_.FullName -NewName "TestScript${Count}$($_.Extension)"; $count++}

Also, note that it is not mandatory to specify the -Begin and -Process parameters. We can simply write:

Get-ChildItem -Recurse C:\Scripts | ForEach-Object { $count = 0} { Rename-Item -Path $_.FullName -NewName "TestScript${Count}$($_.Extension)"; $count++}
Filed in: Columns, Tips and Tricks Tags: ,

2 Responses to "#PSTip ForEach-Object Gotcha!"

  1. GDisk FLa says:

    Not useful example,when the rename cmdlet support scriptblock:

    $i=0;dir *.txt | ren -newname { “TestScript” + ($global:i++) + $_.Extension}

    • Ravikanth says:

      Well, you are right that rename-item has ScriptBlock support. For a beginner, even this may be new. So, the idea was to show ForEach-Object capability than the Rename-Item.

Leave a Reply

Submit Comment

© 2016 PowerShell Magazine. All rights reserved. XHTML / CSS Valid.
Proudly designed by Theme Junkie.
%d bloggers like this: