3

The Rename-Item accepts a scriptblock (I think...) as a parameter so I can do something like this if I want to rename a bunch of files (for example):

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

I'm writing a cmdlet to rename some items and would like to be able to use syntax like this for the new name, but how to declare the parameter to accept a script block like this or a simple string?

Ether
  • 53,118
  • 13
  • 86
  • 159
marcus.greasly
  • 703
  • 4
  • 15

2 Answers2

4

If you are writing a .NET based cmdlet (C#/VB) then keep in mind that any parameter that is pipeline bound will support scriptblocks automatically. That is just a feature on PowerShell. If however, the parameter you want to use isn't pipeline bound then you can do this:

[Parameter]
public ScriptBlock NewName { get; set; }

[Parameter(ValueFromPipeline = true)]
public string OldName { get; set; }

protected override void ProcessRecord()
{
    Collection<PSObject> results = NewName.Invoke(this.OldName);
    this.Host.UI.WriteLine("New name is " + results[0]);
}

The only thing I don't like about this approach is that you can't use $_ in the scriptblock you have to use $args[0] in this case. Perhaps there is a better way to do this and somebody will chime in with it.

OTOH, Rename-Item does specify the NewName parameter as pipeline bound by property name. In this case, you just make the NewName parameter be the type you want (string) and let PowerShell do the scriptblock magic. Best of all, in this case $_ works in the scriptblock e.g.:

[Parameter(ValueFromPipelineByPropertyName = true)]
public string NewName { get; set; }

[Parameter(ValueFromPipeline = true)]
public string OldName { get; set; }

protected override void ProcessRecord()
{
    this.Host.UI.WriteLine("New name is " + this.NewName);
}
Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • Yes, that's what I wanted to do - if the Rename-Item sets the parameter as pipeline bound that's good enough for me. – marcus.greasly Sep 04 '09 at 10:25
  • How have I gotten by this long without knowing that you can use ScriptBlocks in this context?? I would assume that this results in less overhead as compared with wrapping in a ForEach command? – Josh May 18 '12 at 14:22
  • @JoshEinstein I would think it is less overhead. One less cmdlet to run begin/process/end on. – Keith Hill May 18 '12 at 15:04
  • _"any parameter that is pipeline bound will support scriptblocks automatically"_ -- This is explained at [about_Script_Blocks](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-7.1#using-delay-bind-script-blocks-with-parameters) and more in depth in [this answer](https://stackoverflow.com/a/52807680/7571258). – zett42 Apr 14 '21 at 17:46
3

Yes. -NewName takes a script block. Try this:

Function Do-MyRename {
  param([ScriptBlock]$sb)

  dir *.ps1| Rename-Item -NewName $sb
}
Doug Finke
  • 6,675
  • 1
  • 31
  • 47