Welcome to MSDN Blogs Sign in | Join | Help

$MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

A while ago I blogged about the power of Flexible Pipeling Scriptblock parameters.  The mechanics of this are quite simple:  In a pipeline environment, if you provide a SCRIPTBLOCK to a parameter which does not take a SCRIPTBLOCK or an OBJECT, the PowerShell engine assigns the current pipeline object to the variable "$_", runs the scriptblock and uses whatever value it returns for the value of that parameter. 

 

Looking back at this, I don't think we've done a good enough job explaining it or promulgating its use because it is incredibly powerful and I just don't see it being used enough.  Let me give a couple of simple examples of how I used it this morning.  It is a technique that is well worth learning because you can do MIND-WARPING things once you figure out how to leverage it's power.

 

This morning I wanted to email a set of PS1 files to someone.  Exchange blocks PS1 files so what I do is to rename these by added ".txt" to them.  I've see number of people sharing scripts that do something similar using the following technique:

 

foreach ($file in dir *.ps1) {
    $newName = $file.Name + ".TXT"
    copy-item -Path $file.Name -Destination $newName
}

 

That works but heavens, why do all that when you could just use ScriptBlock Parameters?  Now when you start using scriptblocks, you might what to leverage our friend -WHATIF until you are comfortable with how it works (isn't -WHATIF da bomb?).

Dir *.ps1 | Copy-Item -Destination {$_.Name + ".TXT"} -Whatif

 

On the way back, you can do this:

Dir *.ps1.txt |Rename-item -NewName {$_.Name -replace ".txt"} -whatif

 

Now the scriptblock can be as large as you like you can do anything you want in it so feel free to get creative.  Imagine that you wanted to normalize/serialize the names of a set of scripts.  You could do it this way:

$Global:x=0; dir *.ps1 |copy-Item -Destination {"ScriptFile{0:000}" -f $global:x++} -whatif

Here is an fun one:

dir *.ps1 |copy-Item -Destination {if ($_.length -ge 100) {$_.Name + ".BIG"} else {$_.Name + ".SMALL"}} -whatif

 

Experiment and Enjoy!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Published Monday, April 21, 2008 12:59 AM by PowerShellTeam

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Microsoft news and tips » $MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

# re: $MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

It is a great idea to promulgate this. It seems to me that most of PowerShell users including fanatics never heard about this technique.

Will it work in the same way for script cmdlets?

Monday, April 21, 2008 8:05 AM by Roman Kuzmin

# re: $MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

> Will it work in the same way for script cmdlets?

It doesn't right now but it should.

Jeffrey Snover [MSFT]

Windows Management Partner Architect

Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell

Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Monday, April 21, 2008 8:33 AM by PowerShellTeam

# re: $MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

Tuesday, April 22, 2008 9:22 AM by Don Jones

# re: $MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

this here therefore sets the parameter to a different value each time an item comes through the pipeline despite the fact that the cmdlet gets called only once?

Thus it will only behave correctly for cmdlets that read the parameter each item, rather than just reading it in the beginprocess section?

-Karl

Tuesday, April 22, 2008 12:02 PM by karl prosser

# re: $MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

I have been personally evangelizing PowerShell at work and also on my blog for a while now. The more I dig into PowerShell, the more I realize how powerful it is and it will change the face of shell world.

So I made a PowerShell cap.

http://www.devslife.com/2008/04/20/powershell帽/

Though I blog in Japanese, I live near Seattle. If you see me wearing the cap, please say hi to me! :)

Tuesday, April 22, 2008 12:41 PM by vbNullString

# re: $MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

@karl

I haven't tried it, but I don't think scriptblock parameters are much different from pipline parameters. So I guess you have to re-read the parameter for each item in the pipline.

The alternative would be that your CmdLets Begin/Bla/Process methods are called for each pipline item in the case of scriptblock parameters being present. But then your CmdLet would behave rather different in those two scenarios (think about measure-object), so I don't think it works that way.

Wednesday, April 23, 2008 5:30 AM by Maximilian Hänel

# re: $MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

uh.. never heard of this.. madness. Does this work for v1.0 ? (only have v2 ctp here).

Wednesday, April 23, 2008 8:46 PM by Oisin Grehan

# re: $MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

> Does this work for v1.0 ?

Yes.

Jeffrey Snover [MSFT]

Windows Management Partner Architect

Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell

Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Wednesday, April 23, 2008 11:12 PM by PowerShellTeam

# re: $MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

This sounds a lot like what I did with my AD mail enabled object dump scripts.  Being an old crusty Exchange admin I've learned that it's nice to have a directory dump that you can refer back to from time to time, I came up with something like this when Powershell came out and I started using it (abbreviated and sanitized):

$date = get-date -format yyyyMMdd

$fname = "userDump-" + $date + ".csv"

$domain = New-Object System.DirectoryServices.DirectoryEntry

$DN1 = (get-content Z:\Directory\UserDump\DN-import.txt)

$DN2 = $DN1 | foreach {

$domain.PSBase.Children.find($_)

}

filter Format-DumpADUser {

$_ | select @{name='distinguishedName';Expression={$_.distinguishedName} },

@{ name='homeMDB'; Expression={$_.homeMDB} },

@{ name='displayName'; Expression={$_.displayName} },

@{ name='mail'; Expression={$_.mail} },

@{ name='proxyAddresses'; Expression={$_.proxyAddresses} },

@{ name='memberOf'; Expression={$_.memberOf} },

@{ name='whenChanged'; Expression={$_.whenChanged} },

@{ name='whenCreated'; Expression={$_.whenCreated} },

@{ name='extensionAttribute1'; Expression={$_.extensionAttribute1} },

@{ name='mailNickname'; Expression={$_.mailNickname} },

@{ name='mDBUseDefaults'; Expression={$_.mDBUseDefaults} },

@{ name='mDBOverQuotaLimit'; Expression={$_.mDBOverQuotaLimit} },

@{ name='mDBStorageQuota'; Expression={$_.mDBStorageQuota} }

}

$DN2 | foreach {

$_.PSBase.Children

} | Format-DumpADUser | export-csv -NoTypeInformation Z:\Directory\Archive\$fname

Friday, April 25, 2008 7:48 PM by sbq

# re: $MindWarpingPower = $Cmdlets + $ScriptBlock_Parameters

I have a case where a scriptblock parameter is evaluated literally:

Get-DistributionGroup | Set-DistributionGroup -DisplayName {"~$($_.name)"}

The name of the groups are updated with: "~$($_.name)".

Could it be because -DisplayName is a dynamic parameter?

Here is the The relevant trace-command output:

DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args

[Set-DistributionGroup]

DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args

[Set-DistributionGroup]

DEBUG: ParameterBinding Information: 0 : BIND cmd line args to DYNAMIC

parameters.

DEBUG: ParameterBinding Information: 0 : DYNAMIC parameter object:

[Microsoft.Exchange.Data.Directory.Management.DistributionGroup]

DEBUG: ParameterBinding Information: 0 : BIND NAMED args to DYNAMIC

parameters

DEBUG: ParameterBinding Information: 0 : BIND arg ["~$($_.name)"]

to parameter [DisplayName]

DEBUG: ParameterBinding Information: 0 : COERCE arg type

[System.Management.Automation.ScriptBlock]

to

[System.String]

DEBUG: ParameterBinding Information: 0 : CONVERT arg type

to param type using

LanguagePrimitives.ConvertTo

DEBUG: ParameterBinding Information: 0 : CONVERT SUCCESSFUL

using LanguagePrimitives.ConvertTo:

["~$($_.name)"]

DEBUG: ParameterBinding Information: 0 : BIND arg

["~$($_.name)"]

to param [DisplayName] SUCCESSFUL

-----

Shay Levi

$cript Fanatic

http://scriptolog.blogspot.com

Monday, April 28, 2008 4:35 AM by Shay

Leave a Comment

(required) 
required 
(required) 
 
Page view tracker